package com.szcinda.service.dto.zadOrder;

import com.szcinda.controller.Result;
import com.szcinda.controller.params.QueryDto;
import com.szcinda.repository.*;
import com.szcinda.service.DateUtils;
import com.szcinda.service.PageResult;
import com.szcinda.service.SnowFlakeFactory;
import com.szcinda.service.TypeStringUtils;
import com.szcinda.service.dto.analysis.OrderAnalysisServiceImpl;
import com.szcinda.service.dto.calculatePrice.CalculateDto;
import com.szcinda.service.dto.calculatePrice.CalculateService;
import com.szcinda.service.dto.calculatePrice.PriceDto;
import com.szcinda.service.dto.history.ZadOrderExportVo;
import com.szcinda.service.dto.idGenerator.IdGeneratorService;
import com.szcinda.service.dto.log.LogCreateDto;
import com.szcinda.service.dto.log.LogService;
import com.szcinda.service.dto.log.OperateOrderDto;
import com.szcinda.service.dto.organization.OrganizationService;
import com.szcinda.service.dto.role.RoleRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.szcinda.service.dto.ghOrder.GhOrderServiceImpl.isNumber;
import static com.szcinda.service.dto.zadOrder.ZadOrderDto.FeeDto;
import static com.szcinda.service.dto.zadOrder.ZadOrderDto.OrderFollowDto;

@Service
@Transactional
public class ZadOrderServiceImpl implements ZadOrderService {

    private final ZadOrderRepository zadOrderRepository;
    private final LogService logService;
    private final UserRepository userRepository;
    private final BusinessCodeRepository businessCodeRepository;
    private final ClientRepository clientRepository;
    private final CarrierRepository carrierRepository;
    private final OrderFeeRepository orderFeeRepository;
    private final OrganizationService organizationService;
    private final SnowFlakeFactory snowFlakeFactory;
    private final OrderFollowRepository orderFollowRepository;
    private final FeeItemRepository feeItemRepository;
    private final ContainerOrderRepository containerOrderRepository;
    private final BaoGuanFeeRepository baoGuanFeeRepository;
    private final RoleRepository roleRepository;
    private final IdGeneratorService idGeneratorService;
    private final ExportLogRepository exportLogRepository;
    private final CalculateService calculateService;
    private final RateRepository rateRepository;

    public static final DateTimeFormatter df = DateTimeFormatter.ofPattern("yyMMdd");

    public ZadOrderServiceImpl(ZadOrderRepository zadOrderRepository, LogService logService, UserRepository userRepository, BusinessCodeRepository businessCodeRepository, ClientRepository clientRepository, CarrierRepository carrierRepository, OrderFeeRepository orderFeeRepository, OrganizationService organizationService, OrderFollowRepository orderFollowRepository, FeeItemRepository feeItemRepository, ContainerOrderRepository containerOrderRepository, BaoGuanFeeRepository baoGuanFeeRepository, RoleRepository roleRepository, IdGeneratorService idGeneratorService, ExportLogRepository exportLogRepository, CalculateService calculateService, RateRepository rateRepository) {
        this.zadOrderRepository = zadOrderRepository;
        this.logService = logService;
        this.userRepository = userRepository;
        this.businessCodeRepository = businessCodeRepository;
        this.clientRepository = clientRepository;
        this.carrierRepository = carrierRepository;
        this.orderFeeRepository = orderFeeRepository;
        this.organizationService = organizationService;
        this.orderFollowRepository = orderFollowRepository;
        this.feeItemRepository = feeItemRepository;
        this.containerOrderRepository = containerOrderRepository;
        this.baoGuanFeeRepository = baoGuanFeeRepository;
        this.roleRepository = roleRepository;
        this.idGeneratorService = idGeneratorService;
        this.exportLogRepository = exportLogRepository;
        this.calculateService = calculateService;
        this.rateRepository = rateRepository;
        this.snowFlakeFactory = SnowFlakeFactory.getInstance();
    }


    @Override
    public void create(ZadOrderCreateDto createDto) {
        Assert.isTrue(StringUtils.hasText(createDto.getClientId()), "客户必选");
        Assert.isTrue(StringUtils.hasText(createDto.getCalculateType()), "计费方式必选");
        Assert.isTrue(StringUtils.hasText(createDto.getOriginPlace()), "起始站必选");
        Assert.isTrue(StringUtils.hasText(createDto.getDestPlace()), "目的站必选");
        // 自动识别分隔符
        if (createDto.getDestPlace().contains("-")) {
            createDto.setOriginPlace(createDto.getDestPlace().split("-")[0].trim());
            createDto.setDestPlace(createDto.getDestPlace().split("-")[1].trim());
        }
        Assert.isTrue(StringUtils.hasText(createDto.getBusinessCode()), "业务类型必填");
        BusinessCode businessCode = businessCodeRepository.findByName(createDto.getBusinessCode());
        String businessCodePre = "";
        if (businessCode != null) {
            businessCodePre = businessCode.getPrefix();
        }
        User user = userRepository.findById(createDto.getOpId());
        Client client = clientRepository.findById(createDto.getClientId());
        Organization organization = organizationService.getById(createDto.getOrganizationId());
        // 如果订单编号存在，则修改
        if (StringUtils.hasText(createDto.getOrderNumber())) {
            ZadOrder record = zadOrderRepository.findByOrderNumber(createDto.getOrderNumber());
            Assert.isTrue(record != null, String.format("根据订单编号【%s】找不到记录", createDto.getOrderNumber()));
            Assert.isTrue(record.getStatus().equals(OrderStatus.UN_CONFIRM), String.format("订单【%s】已锁定，不能覆盖更新", createDto.getOrderNumber()));
            BeanUtils.copyProperties(createDto, record, "id");
            zadOrderRepository.saveAndFlush(record);
            return;
        }
        int size = createDto.getQuantity();
        // 默认创建一条
        if (size == 0) {
            size = 1;
        }
        // 获取今天共有多少单
        LocalDate now = LocalDate.now();
        Specification<ZadOrder> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), now.atStartOfDay());
            predicates.add(timeStart);
            Predicate timeEnd = criteriaBuilder.lessThan(root.get("createTime"), now.plusDays(1).atStartOfDay());
            predicates.add(timeEnd);
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        List<ZadOrder> historyList = zadOrderRepository.findAll(specification);
        int count = historyList.size();
        for (int i = 0; i < size; i++) {
            ZadOrder zadOrder = new ZadOrder();
            BeanUtils.copyProperties(createDto, zadOrder);
            String prefix = "";
            if (StringUtils.hasText(organization.getPrefix())) {
                prefix = organization.getPrefix();
            }
            // 生成下一个单号
            String baseNumber;
            while (true) {
                baseNumber = prefix + businessCodePre + df.format(LocalDateTime.now()) + String.format("%03d", count + 1);
                String finalBaseNumber = baseNumber;
                if (historyList.stream().noneMatch(re -> re.getOrderNumber().equals(finalBaseNumber))) {
                    break;
                } else {
                    count++;
                }
            }
            ZadOrder record = zadOrderRepository.findByOrderNumber(baseNumber);
            Assert.isTrue(record == null, String.format("存在相同订单编号【%s】的国际班列", baseNumber));
            zadOrder.setOrderNumber(baseNumber);
            zadOrder.setQuantity(1);
            zadOrder.setClientName(client.getName());
            zadOrder.setOrganizationName(organization.getName());
            zadOrder.setId(snowFlakeFactory.nextId("ZO"));
            zadOrder.setUserId(user.getId());
            zadOrder.setUserName(user.getNickName());
            zadOrder.setStatus(OrderStatus.UN_CONFIRM);
            zadOrderRepository.saveAndFlush(zadOrder);

            BaoGuanFee baoGuanFee = baoGuanFeeRepository.findByBelongId(zadOrder.getId());
            if (baoGuanFee == null) {
                baoGuanFee = new BaoGuanFee();
                baoGuanFee.setId(snowFlakeFactory.nextId("BG"));
                baoGuanFee.setBelongId(zadOrder.getId());
            }
            baoGuanFee.setHomeFee(createDto.getHomeFee());
            baoGuanFee.setSheetFee(createDto.getSheetFee());
            baoGuanFee.setHomeCount(createDto.getHomeCount());
            baoGuanFee.setSheetCount(createDto.getSheetCount());
            baoGuanFeeRepository.save(baoGuanFee);

            // 应收
            List<ZadOrderCreateDto.FeeDto> feeList = createDto.getFeeList();
            if (feeList != null && feeList.size() > 0) {
                List<OrderFee> fees = new ArrayList<>();
                feeList.forEach(feeDto -> {
                    OrderFee fee = new OrderFee();
                    BeanUtils.copyProperties(feeDto, fee);
                    fee.setId(snowFlakeFactory.nextId("OF"));
                    fee.setOrderId(zadOrder.getId());
                    fee.setType(TypeStringUtils.feeInType);
                    fee.setStatus(TypeStringUtils.feeUnLockStatus);
                    fee.setCurrency(feeDto.getCurrency());
                    if (zadOrder.getActDepartureTime() != null) {
                        // 以发车时间作为创建时间，后面统计需要按照发车时间统计，设置为发车时间方便汇总
                        fee.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                    }
                    fees.add(fee);
                    // 检查费用科目是否存在，如果不存在则新增
                    FeeItem feeItem = feeItemRepository.findByName(fee.getFeeName().trim());
                    if (StringUtils.hasText(fee.getFeeName()) && fee.getFeeName().trim().length() > 0 && feeItem == null && "报关费".equals(fee.getFeeName().trim()) && "报关联单费".equals(fee.getFeeName().trim()) && "超期堆存费".equals(fee.getFeeName().trim())) {
                        feeItem = new FeeItem();
                        feeItem.setId(snowFlakeFactory.nextId("FI"));
                        feeItem.setName(fee.getFeeName());
                        feeItemRepository.save(feeItem);
                    }
                });
                orderFeeRepository.save(fees);
            }
            HashSet<String> carrierNames = new HashSet<>();
            // 应付
            List<ZadOrderCreateDto.FeeDto> outFeeList = createDto.getOutFeeList();
            if (outFeeList != null && outFeeList.size() > 0) {
                List<OrderFee> fees = new ArrayList<>();
                outFeeList.forEach(feeDto -> {
                    OrderFee fee = new OrderFee();
                    BeanUtils.copyProperties(feeDto, fee);
                    fee.setId(snowFlakeFactory.nextId("OF"));
                    fee.setOrderId(zadOrder.getId());
                    fee.setType(TypeStringUtils.feeOutType);
                    fee.setCarrierId(feeDto.getCarrierId());
                    if (StringUtils.hasText(feeDto.getCarrierId())) {
                        carrierNames.add(feeDto.getCarrierId());
                    }
                    if (zadOrder.getActDepartureTime() != null) {
                        // 以发车时间作为创建时间，后面统计需要按照发车时间统计，设置为发车时间方便汇总
                        fee.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                    }
                    fee.setStatus(TypeStringUtils.feeUnLockStatus);
                    fee.setCurrency(feeDto.getCurrency());
                    fee.setCarrierId(feeDto.getCarrierId());
                    fees.add(fee);
                    // 检查费用科目是否存在，如果不存在则新增
                    FeeItem feeItem = feeItemRepository.findByName(fee.getFeeName().trim());
                    if (StringUtils.hasText(fee.getFeeName()) && fee.getFeeName().trim().length() > 0 && feeItem == null && "报关费".equals(fee.getFeeName().trim()) && "报关联单费".equals(fee.getFeeName().trim()) && "超期堆存费".equals(fee.getFeeName().trim())) {
                        feeItem = new FeeItem();
                        feeItem.setId(snowFlakeFactory.nextId("FI"));
                        feeItem.setName(fee.getFeeName());
                        feeItemRepository.save(feeItem);
                    }
                });
                orderFeeRepository.save(fees);
            }
            zadOrder.setCarrierName(String.join(",", carrierNames));
            // 判断柜号和用箱天数 订单线路出现COC
            if ((zadOrder.getOriginPlace() != null && zadOrder.getOriginPlace().toUpperCase().contains("COC")) || (zadOrder.getDestPlace() != null && zadOrder.getDestPlace().toUpperCase().contains("COC"))) {
                if (StringUtils.hasText(zadOrder.getCabinetNumber())) {
                    // 检查是否存在相同未还的箱子
                    ContainerOrder containerOrder = containerOrderRepository.findByOrderId(zadOrder.getId());
                    if (containerOrder == null) {
                        // 需要生成集装箱记录
                        containerOrder = new ContainerOrder();
                        BeanUtils.copyProperties(zadOrder, containerOrder);
                        int nextNumber = idGeneratorService.getNextNumber("containerOrder");
                        containerOrder.setBusinessNumber(client.getCode() + df.format(LocalDateTime.now()) + String.format("%04d", nextNumber));
                        containerOrder.setOrderNumber(zadOrder.getOrderNumber());
                        containerOrder.setId(snowFlakeFactory.nextId("CO"));
                        containerOrder.setOrderId(zadOrder.getId());
                        containerOrderRepository.save(containerOrder);
                    }
                }
            }
            zadOrderRepository.save(zadOrder);
        }
    }

    @Override
    public void update(ZadOrderUpdateDto updateDto) {
        ZadOrder zadOrder = zadOrderRepository.findById(updateDto.getId());
        Assert.isTrue(!OrderStatus.CONFIRMED.equals(zadOrder.getStatus()), "订单已锁定，不可编辑");
        BeanUtils.copyProperties(updateDto, zadOrder, "id");
        Client client = clientRepository.findById(updateDto.getClientId());
        Assert.isTrue(client != null, "客户必选");
        Organization organization = organizationService.getById(zadOrder.getOrganizationId());
        // 查找是否存在相同的柜号
        if (updateDto.getFollowDtos().size() > 0 && StringUtils.hasText(updateDto.getFollowDtos().get(0).getCabinetNumber())) {
            // 查找是否存在相同的柜号
            // 机构、客户、柜号、订舱号、订舱人、业务类型相同时，其他部分直接覆盖
            List<ZadOrder> zadOrders = zadOrderRepository.findAllByOptions(updateDto.getOrganizationId(), updateDto.getClientId(), updateDto.getCabinetNumber(), updateDto.getBookNumber(), updateDto.getBookName(), updateDto.getBusinessCode(), updateDto.getOriginPlace(), updateDto.getDestPlace());
            boolean hasSameRecord = false;
            if (zadOrders != null && zadOrders.size() > 0) {
                hasSameRecord = zadOrders.stream().anyMatch(z -> !z.getId().equals(updateDto.getId()));
            }
            Assert.isTrue(!hasSameRecord, String.format("机构【%s】存在相同客户【%s】相同柜号【%s】相同订舱号【%s】相同订舱人【%s】相同业务类型【%s】相同线路【%s-%s】的记录", organization.getName(), client.getName(), updateDto.getCabinetNumber(), updateDto.getBookNumber(), updateDto.getBookName(), updateDto.getBusinessCode(), updateDto.getOriginPlace(), updateDto.getDestPlace()));
        }
        zadOrder.setClientName(client.getName());
        List<ZadOrderUpdateDto.OrderFollowDto> followDtos = updateDto.getFollowDtos();
        zadOrder.setOrganizationName(organization.getName());
        zadOrder.setCabinetNumber(followDtos.get(0).getCabinetNumber());
        zadOrderRepository.save(zadOrder);

        BaoGuanFee baoGuanFee = baoGuanFeeRepository.findByBelongId(zadOrder.getId());
        if (baoGuanFee == null) {
            baoGuanFee = new BaoGuanFee();
            baoGuanFee.setId(snowFlakeFactory.nextId("BG"));
            baoGuanFee.setBelongId(zadOrder.getId());
        }
        baoGuanFee.setHomeFee(updateDto.getHomeFee());
        baoGuanFee.setSheetFee(updateDto.getSheetFee());
        baoGuanFee.setHomeCount(updateDto.getHomeCount());
        baoGuanFee.setSheetCount(updateDto.getSheetCount());
        baoGuanFeeRepository.save(baoGuanFee);

        orderFollowRepository.deleteByOrderId(zadOrder.getId());
        List<OrderFollow> follows = new ArrayList<>();
        followDtos.forEach(dto -> {
            OrderFollow follow = new OrderFollow();
            BeanUtils.copyProperties(dto, follow);
            follow.setOrganizationId(zadOrder.getOrganizationId());
            follow.setId(snowFlakeFactory.nextId("OF"));
            follow.setOrderId(zadOrder.getId());
            follow.setOrganizationId(zadOrder.getOrganizationId());
            follow.setCabinetNumber(followDtos.get(0).getCabinetNumber());
            follows.add(follow);
            // 更新集装箱的提箱日期
            ContainerOrder containerOrder = containerOrderRepository.findByOrderId(zadOrder.getId());
            if (containerOrder != null) {
                containerOrder.setPickDate(updateDto.getPickDate());
                containerOrderRepository.save(containerOrder);
            }
        });
        orderFollowRepository.save(follows);
        // 清除旧费用
        orderFeeRepository.deleteByOrderId(updateDto.getId());
        // 应收
        List<ZadOrderUpdateDto.FeeDto> feeList = updateDto.getFeeList();
        if (feeList != null && feeList.size() > 0) {
            List<OrderFee> fees = new ArrayList<>();
            feeList.forEach(feeDto -> {
                OrderFee fee = new OrderFee();
                BeanUtils.copyProperties(feeDto, fee);
                fee.setId(snowFlakeFactory.nextId("OF"));
                fee.setOrderId(zadOrder.getId());
                fee.setType(TypeStringUtils.feeInType);
                if (zadOrder.getActDepartureTime() != null) {
                    // 以发车时间作为创建时间，后面统计需要按照发车时间统计，设置为发车时间方便汇总
                    fee.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                }
                fee.setStatus(feeDto.getStatus());
                fee.setCurrency(feeDto.getCurrency());
                fees.add(fee);
                // 检查费用科目是否存在，如果不存在则新增
                FeeItem feeItem = feeItemRepository.findByName(fee.getFeeName().trim());
                if (feeItem == null && StringUtils.hasText(fee.getFeeName()) && fee.getFeeName().trim().length() > 0 && (!"报关费".equals(fee.getFeeName().trim()) && !"报关联单费".equals(fee.getFeeName().trim()) && !"超期堆存费".equals(fee.getFeeName().trim()))) {
                    feeItem = new FeeItem();
                    feeItem.setId(snowFlakeFactory.nextId("FI"));
                    feeItem.setName(fee.getFeeName());
                    feeItemRepository.save(feeItem);
                }
            });
            orderFeeRepository.save(fees);
        }
        HashSet<String> carrierNames = new HashSet<>();
        // 应付
        List<ZadOrderUpdateDto.FeeDto> outFeeList = updateDto.getOutFeeList();
        if (outFeeList != null && outFeeList.size() > 0) {
            List<OrderFee> fees = new ArrayList<>();
            outFeeList.forEach(feeDto -> {
                OrderFee fee = new OrderFee();
                BeanUtils.copyProperties(feeDto, fee);
                fee.setId(snowFlakeFactory.nextId("OF"));
                fee.setOrderId(zadOrder.getId());
                fee.setType(TypeStringUtils.feeOutType);
                fee.setCarrierId(feeDto.getCarrierId());
                if (StringUtils.hasText(feeDto.getCarrierId())) {
                    carrierNames.add(feeDto.getCarrierId());
                }
                fee.setStatus(feeDto.getStatus());
                fee.setCurrency(feeDto.getCurrency());
                fee.setCarrierId(feeDto.getCarrierId());
                if (StringUtils.isEmpty(fee.getStatus())) {
                    fee.setStatus(TypeStringUtils.feeUnLockStatus);
                }
                if (zadOrder.getActDepartureTime() != null) {
                    // 以发车时间作为创建时间，后面统计需要按照发车时间统计，设置为发车时间方便汇总
                    fee.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                }
                fees.add(fee);
                // 检查费用科目是否存在，如果不存在则新增
                FeeItem feeItem = feeItemRepository.findByName(fee.getFeeName().trim());
                if (feeItem == null && StringUtils.hasText(fee.getFeeName()) && fee.getFeeName().trim().length() > 0 && (!"报关费".equals(fee.getFeeName().trim()) && !"报关联单费".equals(fee.getFeeName().trim()) && !"超期堆存费".equals(fee.getFeeName().trim()))) {
                    feeItem = new FeeItem();
                    feeItem.setId(snowFlakeFactory.nextId("FI"));
                    feeItem.setName(fee.getFeeName());
                    feeItemRepository.save(feeItem);
                }
            });
            orderFeeRepository.save(fees);
        }
        zadOrder.setCarrierName(String.join(",", carrierNames));
        zadOrderRepository.save(zadOrder);
        // 判断柜号和用箱天数 订单线路出现COC
        if ((zadOrder.getOriginPlace() != null && zadOrder.getOriginPlace().toUpperCase().contains("COC")) || (zadOrder.getDestPlace() != null && zadOrder.getDestPlace().toUpperCase().contains("COC"))) {
            if (StringUtils.hasText(zadOrder.getCabinetNumber())) {
                // 检查是否存在相同未还的箱子
                ContainerOrder containerOrder = containerOrderRepository.findByOrderId(zadOrder.getId());
                if (containerOrder == null) {
                    // 需要生成集装箱记录
                    containerOrder = new ContainerOrder();
                    BeanUtils.copyProperties(zadOrder, containerOrder);
                    int nextNumber = idGeneratorService.getNextNumber("containerOrder");
                    containerOrder.setBusinessNumber(client.getCode() + df.format(LocalDateTime.now()) + String.format("%04d", nextNumber));
                    containerOrder.setOrderNumber(zadOrder.getOrderNumber());
                    containerOrder.setId(snowFlakeFactory.nextId("CO"));
                    containerOrder.setOrderId(zadOrder.getId());
                    containerOrderRepository.save(containerOrder);
                }
            }
        }
        User user = userRepository.findById(updateDto.getOpId());
        LogCreateDto logCreateDto = new LogCreateDto(user.getId(), user.getNickName(), "修改中欧/东南亚班列", zadOrder.getOrderNumber());
        logService.create(logCreateDto);
    }

    @Override
    public void update(ZadOrderUpdatePartDto updateDto) {
        ZadOrder zadOrder = zadOrderRepository.findById(updateDto.getId());
        Assert.isTrue(!OrderStatus.CONFIRMED.equals(zadOrder.getStatus()), "订单已锁定，不可编辑");
        Client client = clientRepository.findById(updateDto.getClientId());
        Assert.isTrue(client != null, "客户必选");
        if (StringUtils.hasText(updateDto.getCabinetNumber())) {
            Organization organization = organizationService.getById(zadOrder.getOrganizationId());
            // 查找是否存在相同的柜号
            // 机构、客户、柜号、订舱号、订舱人、业务类型相同时，其他部分直接覆盖
            List<ZadOrder> zadOrders = zadOrderRepository.findAllByOptions(updateDto.getOrganizationId(), updateDto.getClientId(), updateDto.getCabinetNumber(), updateDto.getBookNumber(), updateDto.getBookName(), updateDto.getBusinessCode(), updateDto.getOriginPlace(), updateDto.getDestPlace());
            boolean hasSameRecord = false;
            if (zadOrders != null && zadOrders.size() > 0) {
                hasSameRecord = zadOrders.stream().anyMatch(z -> !z.getId().equals(updateDto.getId()));
            }
            Assert.isTrue(!hasSameRecord, String.format("机构【%s】存在相同客户【%s】相同柜号【%s】相同订舱号【%s】相同订舱人【%s】相同业务类型【%s】相同线路【%s-%s】的记录", organization.getName(), client.getName(), updateDto.getCabinetNumber(), updateDto.getBookNumber(), updateDto.getBookName(), updateDto.getBusinessCode(), updateDto.getOriginPlace(), updateDto.getDestPlace()));
        }
        BeanUtils.copyProperties(updateDto, zadOrder, "id");
        if ("IN_OR_SETTLEMENT".equals(updateDto.getType()) && BillType.STATUS_YJS.equals(zadOrder.getSettlement())) {
            // 集装箱数据没有实际还箱日期，锁账需要弹窗提醒
            if (StringUtils.hasText(zadOrder.getCabinetNumber())) {
                ContainerOrder containerOrder = containerOrderRepository.findByOrderId(zadOrder.getId());
                Assert.isTrue(containerOrder == null || containerOrder.getActReturnDate() != null, String.format("关联的集装箱【%s】没有实际还箱日期", zadOrder.getCabinetNumber()));
            }
        }
        zadOrderRepository.save(zadOrder);
        // 判断柜号和用箱天数 订单线路出现COC
        if ((zadOrder.getOriginPlace() != null && zadOrder.getOriginPlace().toUpperCase().contains("COC")) || (zadOrder.getDestPlace() != null && zadOrder.getDestPlace().toUpperCase().contains("COC"))) {
            if (StringUtils.hasText(zadOrder.getCabinetNumber())) {
                // 检查是否存在相同未还的箱子
                ContainerOrder containerOrder = containerOrderRepository.findByOrderId(zadOrder.getId());
                if (containerOrder == null) {
                    // 需要生成集装箱记录
                    containerOrder = new ContainerOrder();
                    BeanUtils.copyProperties(zadOrder, containerOrder);
                    containerOrder.setOrderNumber(zadOrder.getOrderNumber());
                    int nextNumber = idGeneratorService.getNextNumber("containerOrder");
                    containerOrder.setBusinessNumber(client.getCode() + df.format(LocalDateTime.now()) + String.format("%04d", nextNumber));
                    containerOrder.setId(snowFlakeFactory.nextId("CO"));
                    containerOrder.setOrderId(zadOrder.getId());
                    containerOrderRepository.save(containerOrder);
                }
            }
        }
        User op = userRepository.findById(updateDto.getOpId());
        if ("IN_OR_SETTLEMENT".equals(updateDto.getType()) && BillType.STATUS_YKP.equals(zadOrder.getInvoice())) {
            LogCreateDto logCreateDto = new LogCreateDto(op.getId(), op.getNickName(), "班列订单应收已开票", zadOrder.getOrderNumber());
            logService.create(logCreateDto);
        }
        if ("IN_OR_SETTLEMENT".equals(updateDto.getType()) && BillType.STATUS_YJS.equals(zadOrder.getSettlement())) {
            LogCreateDto logCreateDto = new LogCreateDto(op.getId(), op.getNickName(), "班列订单应收已结算", zadOrder.getOrderNumber());
            logService.create(logCreateDto);
            // 把订单状态锁定
            zadOrder.setStatus(OrderStatus.CONFIRMED);
            zadOrderRepository.save(zadOrder);
            // 把所有应收费用锁定
            List<OrderFee> fees = orderFeeRepository.findByOrderIdAndType(zadOrder.getId(), TypeStringUtils.feeInType);
            for (OrderFee fee : fees) {
                fee.setStatus(TypeStringUtils.feeLockStatus);
            }
            orderFeeRepository.save(fees);
        }
        if ("IN_OR_SETTLEMENT".equals(updateDto.getType()) && BillType.STATUS_YKP.equals(zadOrder.getOutInvoice())) {
            LogCreateDto logCreateDto = new LogCreateDto(op.getId(), op.getNickName(), "班列订单应付已开票", zadOrder.getOrderNumber());
            logService.create(logCreateDto);
        }
        if ("IN_OR_SETTLEMENT".equals(updateDto.getType()) && BillType.STATUS_YJS.equals(zadOrder.getOutSettlement())) {
            LogCreateDto logCreateDto = new LogCreateDto(op.getId(), op.getNickName(), "班列订单应付已结算", zadOrder.getOrderNumber());
            logService.create(logCreateDto);
            // 把订单状态锁定
            zadOrder.setStatus(OrderStatus.CONFIRMED);
            zadOrderRepository.save(zadOrder);
            // 把所有应付费用锁定
            List<OrderFee> fees = orderFeeRepository.findByOrderIdAndType(zadOrder.getId(), TypeStringUtils.feeOutType);
            for (OrderFee fee : fees) {
                fee.setStatus(TypeStringUtils.feeLockStatus);
            }
            orderFeeRepository.save(fees);
        }
        List<OrderFollow> follows = orderFollowRepository.findByOrderId(updateDto.getId());
        OrderFollow follow;
        if (follows.size() > 0) {
            follow = follows.get(0);
        } else {
            follow = new OrderFollow();
            follow.setOrderId(updateDto.getId());
            follow.setId(snowFlakeFactory.nextId("OF"));
            follow.setOrganizationId(zadOrder.getOrganizationId());
        }
        if (updateDto.getFollowDtos() != null && updateDto.getFollowDtos().size() > 0) {
            BeanUtils.copyProperties(updateDto.getFollowDtos().get(0), follow);
        }
        follow.setCabinetNumber(updateDto.getCabinetNumber());
        orderFollowRepository.save(follow);
    }

    @Override
    public void delete(String id, String userId) {
        ZadOrder zadOrder = zadOrderRepository.findById(id);
        zadOrderRepository.delete(zadOrder);
        User user = userRepository.findById(userId);
        LogCreateDto logCreateDto = new LogCreateDto(user.getId(), user.getNickName(), "删除中欧/东南亚班列", zadOrder.getOrderNumber());
        logService.create(logCreateDto);
    }

    @Override
    public PageResult<ZadOrderDto> query(ZadOrderQueryDto params) {
        Assert.hasText(params.getUserId(), "当前操作用户参数必填");
        Specification<ZadOrder> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            User user = userRepository.findById(params.getUserId());
            Role role = roleRepository.findById(user.getRoleId());
            if (RoleType.SUPER_ADMIN.equals(role.getName())) {
                // 得到所有的订单
                List<String> orgIds = organizationService.getAllOrgIds();
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
                if (!StringUtils.isEmpty(params.getOrganizationId())) {
                    Predicate organizationId = criteriaBuilder.equal(root.get("organizationId").as(String.class), params.getOrganizationId());
                    predicates.add(organizationId);
                }
            } else if (RoleType.ADMIN.equals(role.getName())) {
                // 得到当前机构及子机构的所有订单
                List<String> orgIds = organizationService.getChildOrgIds(user.getOrganizationId());
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
                if (!StringUtils.isEmpty(params.getOrganizationId())) {
                    Predicate organizationId = criteriaBuilder.equal(root.get("organizationId"), params.getOrganizationId());
                    predicates.add(organizationId);
                }
            } else if (role.isEqualAdmin()) {
                // 授予了机构管理员权限
                List<String> orgIds = organizationService.getChildOrgIds(user.getOrganizationId());
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
                if (!StringUtils.isEmpty(params.getOrganizationId())) {
                    Predicate organizationId = criteriaBuilder.equal(root.get("organizationId"), params.getOrganizationId());
                    predicates.add(organizationId);
                }
            } else {
                // 得到属于用户当前机构的订单
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(Collections.singletonList(params.getOrganizationId())));
            }
            boolean isAdmin = RoleType.SUPER_ADMIN.equals(role.getName()) || RoleType.ADMIN.equals(role.getName()) || role.isEqualAdmin();
            if (!isAdmin) {
                // 如果不是管理员，只能看到自己的
                Predicate userId = criteriaBuilder.equal(root.get("userId"), params.getUserId());
                predicates.add(userId);
            }
            if (!StringUtils.isEmpty(params.getInvoice())) {
                Predicate organizationId = criteriaBuilder.equal(root.get("invoice").as(String.class), params.getInvoice());
                predicates.add(organizationId);
            }
            if (!StringUtils.isEmpty(params.getSettlement())) {
                Predicate organizationId = criteriaBuilder.equal(root.get("settlement").as(String.class), params.getSettlement());
                predicates.add(organizationId);
            }
            if (!StringUtils.isEmpty(params.getOutInvoice())) {
                Predicate organizationId = criteriaBuilder.equal(root.get("outInvoice").as(String.class), params.getOutInvoice());
                predicates.add(organizationId);
            }
            if (!StringUtils.isEmpty(params.getOutSettlement())) {
                Predicate organizationId = criteriaBuilder.equal(root.get("outSettlement").as(String.class), params.getOutSettlement());
                predicates.add(organizationId);
            }
            if (StringUtils.hasText(params.getDaiBan())) {
                //封装or语句
                List<Predicate> listOr = new ArrayList<>();
                if (TypeStringUtils.feeInType.equals(params.getType())) {
                    Predicate invoice = criteriaBuilder.equal(root.get("invoice").as(String.class), BillType.STATUS_WKP);
                    listOr.add(invoice);
                    Predicate settlement = criteriaBuilder.equal(root.get("settlement").as(String.class), BillType.STATUS_WJS);
                    listOr.add(settlement);
                } else {
                    Predicate outInvoice = criteriaBuilder.equal(root.get("outInvoice").as(String.class), BillType.STATUS_WKP);
                    listOr.add(outInvoice);
                    Predicate outSettlement = criteriaBuilder.equal(root.get("outSettlement").as(String.class), BillType.STATUS_WJS);
                    listOr.add(outSettlement);
                }
                Predicate[] arrayOr = new Predicate[listOr.size()];
                Predicate pre_Or = criteriaBuilder.or(listOr.toArray(arrayOr));
                predicates.add(pre_Or);
            }
            if (StringUtils.hasText(params.getOrderDaiBan())) {
                Predicate alert = criteriaBuilder.equal(root.get("alert"), Boolean.TRUE);
                predicates.add(alert);
            }
            if (!StringUtils.isEmpty(params.getClientId())) {
                Predicate code = criteriaBuilder.equal(root.get("clientId").as(String.class), params.getClientId());
                predicates.add(code);
            }
            if (!StringUtils.isEmpty(params.getCarrierId())) {
                Carrier carrier = carrierRepository.findById(params.getCarrierId());
                Predicate carrierId = criteriaBuilder.like(root.get("carrierName").as(String.class), "%" + carrier.getName() + "%");
                predicates.add(carrierId);
            }
            if (!StringUtils.isEmpty(params.getBookName())) {
                Predicate bookName = criteriaBuilder.like(root.get("bookName").as(String.class), "%" + params.getBookName().trim() + "%");
                predicates.add(bookName);
            }
            if (!StringUtils.isEmpty(params.getOriginPlace())) {
                Predicate originPlace = criteriaBuilder.like(root.get("originPlace").as(String.class), "%" + params.getOriginPlace().trim() + "%");
                predicates.add(originPlace);
            }
            if (!StringUtils.isEmpty(params.getCarrierDeparturePort())) {
                Predicate carrierDeparturePort = criteriaBuilder.like(root.get("carrierDeparturePort").as(String.class), "%" + params.getCarrierDeparturePort().trim() + "%");
                predicates.add(carrierDeparturePort);
            }
            if (!StringUtils.isEmpty(params.getDestPlace())) {
                Predicate destPlace = criteriaBuilder.like(root.get("destPlace").as(String.class), "%" + params.getDestPlace().trim() + "%");
                predicates.add(destPlace);
            }
            if (!StringUtils.isEmpty(params.getUserName())) {
                Predicate userName = criteriaBuilder.like(root.get("userName").as(String.class), "%" + params.getUserName().trim() + "%");
                predicates.add(userName);
            }
            if (!StringUtils.isEmpty(params.getCabinetNumber())) {
                Predicate cabinetNumberArr = criteriaBuilder.like(root.get("cabinetNumber").as(String.class), "%" + params.getCabinetNumber().trim() + "%");
                predicates.add(cabinetNumberArr);
            }
            if (!StringUtils.isEmpty(params.getOrderNumber())) {
                Predicate orderNumber = criteriaBuilder.like(root.get("orderNumber").as(String.class), "%" + params.getOrderNumber().trim() + "%");
                predicates.add(orderNumber);
            }
            if (!StringUtils.isEmpty(params.getBusinessNumber())) {
                Predicate businessNumber = criteriaBuilder.like(root.get("businessNumber").as(String.class), "%" + params.getBusinessNumber().trim() + "%");
                predicates.add(businessNumber);
            }
            if (!StringUtils.isEmpty(params.getBookNumber())) {
                Predicate bookNumber = criteriaBuilder.like(root.get("bookNumber").as(String.class), "%" + params.getBookNumber().trim() + "%");
                predicates.add(bookNumber);
            }
            if (params.getPickDateStart() != null) {
                Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("pickDate"), params.getPickDateStart());
                predicates.add(timeStart);
            }
            if (params.getPickDateEnd() != null) {
                Predicate timeEnd = criteriaBuilder.lessThan(root.get("pickDate"), params.getPickDateEnd().plusDays(1));
                predicates.add(timeEnd);
            }
            if (params.getPreDepartureTimeStart() != null) {
                Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("preDepartureTime"), params.getPreDepartureTimeStart());
                predicates.add(timeStart);
            }
            if (params.getPreDepartureTimeEnd() != null) {
                Predicate timeEnd = criteriaBuilder.lessThan(root.get("preDepartureTime"), params.getPreDepartureTimeEnd().plusDays(1));
                predicates.add(timeEnd);
            }
            if (params.getCreateTimeStart() != null) {
                Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), params.getCreateTimeStart().atStartOfDay());
                predicates.add(timeStart);
            }
            if (params.getCreateTimeEnd() != null) {
                Predicate timeEnd = criteriaBuilder.lessThan(root.get("createTime"), params.getCreateTimeEnd().plusDays(1).atStartOfDay());
                predicates.add(timeEnd);
            }
            if (!StringUtils.isEmpty(params.getAlert())) {
                Predicate alert = criteriaBuilder.equal(root.get("alert"), params.getAlert());
                predicates.add(alert);
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort order = new Sort(Sort.Direction.DESC, "createTime", "orderNumber");
        Pageable pageable = new PageRequest(params.getPage() - 1, params.getPageSize(), order);
        Page<ZadOrder> details = zadOrderRepository.findAll(specification, pageable);
        List<ZadOrder> zadOrders = details.getContent();
        List<String> ids = zadOrders.stream().map(ZadOrder::getId).collect(Collectors.toList());
        List<BaoGuanFee> baoGuanFees = baoGuanFeeRepository.findByBelongIdIn(ids);
        List<OrderFollow> follows = orderFollowRepository.findByOrderIdIn(ids);
        List<OrderFee> fees = orderFeeRepository.findByOrderIdIn(ids);
        List<Carrier> carriers = carrierRepository.findAll();
        List<ZadOrderDto> dtos = new ArrayList<>();
        zadOrders.forEach(zadOrder -> {
            ZadOrderDto dto = new ZadOrderDto();
            BeanUtils.copyProperties(zadOrder, dto);
            dto.setAlert(false);
            BaoGuanFee guanFee = baoGuanFees.stream().filter(baoGuanFee -> baoGuanFee.getBelongId().equals(zadOrder.getId())).findFirst().orElse(null);
            if (guanFee != null) {
                dto.setHomeFee(guanFee.getHomeFee());
                dto.setHomeCount(guanFee.getHomeCount());
                dto.setSheetFee(guanFee.getSheetFee());
                dto.setSheetCount(guanFee.getSheetCount());
            }
            List<OrderFollow> orderFollows = follows.stream().filter(follow -> follow.getOrderId().equals(zadOrder.getId())).collect(Collectors.toList());
            if (orderFollows.size() == 0) {
                OrderFollowDto followDto = new OrderFollowDto();
                dto.getFollowDtos().add(followDto);
            } else {
                orderFollows.forEach(follow -> {
                    OrderFollowDto followDto = new OrderFollowDto();
                    BeanUtils.copyProperties(follow, followDto);
                    dto.getFollowDtos().add(followDto);
                });
            }

            List<OrderFee> orderFees = fees.stream().filter(orderFee -> orderFee.getOrderId().equals(zadOrder.getId())).collect(Collectors.toList());
            if (orderFees.size() > 0) {
                List<FeeDto> feeDtos = new ArrayList<>();
                List<FeeDto> outFeeDtos = new ArrayList<>();
                orderFees.forEach(orderFee -> {
                    FeeDto feeDto = new FeeDto();
                    BeanUtils.copyProperties(orderFee, feeDto);
                    if (TypeStringUtils.feeInType.equals(orderFee.getType())) {
                        feeDtos.add(feeDto);
                    } else {
                        carriers.stream().filter(carrier -> carrier.getId().equals(feeDto.getCarrierId())).findFirst().ifPresent(carrier -> feeDto.setCarrierId(carrier.getName()));
                        outFeeDtos.add(feeDto);
                    }
                });
                dto.setFeeList(feeDtos);
                dto.setOutFeeList(outFeeDtos);
                // 判断是否需要橙色提醒 班列日期在前5天还没填完必要信息，则提醒
                if (dto.getPreDepartureTime() != null) {
                    LocalDate now = LocalDate.now();
                    if (now.isAfter(dto.getPreDepartureTime()) && noFinish(orderFollows)) {
                        dto.setAlert(true);
                    } else {
                        Period between = Period.between(now, dto.getPreDepartureTime());
                        if (Math.abs(between.getDays()) <= 5 && noFinish(orderFollows)) {
                            dto.setAlert(true);
                        }
                    }
                }
            }
            dtos.add(dto);
        });
        return PageResult.of(dtos, params.getPage(), params.getPageSize(), details.getTotalElements());
    }


    private boolean noFinish(List<OrderFollow> orderFollows) {
        if (orderFollows == null || orderFollows.size() == 0) {
            return true;
        }
        OrderFollow orderFollow = orderFollows.get(0);
        if (orderFollow == null) {
            return true;
        }
        String confirm = "已审核完成";
        return StringUtils.isEmpty(orderFollow.getCabinetNumber()) || !confirm.equals(orderFollow.getPackagePic()) || !confirm.equals(orderFollow.getCustomsFile()) || !confirm.equals(orderFollow.getCarFile()) || !confirm.equals(orderFollow.getPackageSolution()) || !confirm.equals(orderFollow.getStFile()) || !confirm.equals(orderFollow.getPreOrderConfirm()) || !confirm.equals(orderFollow.getCustomsReleaseOrder());
    }

    @Override
    public List<ZadOrderDto> queryAll(ZadOrderQueryDto params) {
        Specification<ZadOrder> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            User user = userRepository.findById(params.getUserId());
            Role role = roleRepository.findById(user.getRoleId());
            if (RoleType.SUPER_ADMIN.equals(role.getName())) {
                // 得到所有的订单
                List<String> orgIds = organizationService.getAllOrgIds();
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
                if (!StringUtils.isEmpty(params.getOrganizationId())) {
                    Predicate organizationId = criteriaBuilder.equal(root.get("organizationId").as(String.class), params.getOrganizationId());
                    predicates.add(organizationId);
                }
            } else if (RoleType.ADMIN.equals(role.getName())) {
                // 得到当前机构及子机构的所有订单
                List<String> orgIds = organizationService.getChildOrgIds(user.getOrganizationId());
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
                if (!StringUtils.isEmpty(params.getOrganizationId())) {
                    Predicate organizationId = criteriaBuilder.equal(root.get("organizationId"), params.getOrganizationId());
                    predicates.add(organizationId);
                }
            } else if (role.isEqualAdmin()) {
                // 授予了机构管理员权限
                List<String> orgIds = organizationService.getChildOrgIds(user.getOrganizationId());
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
                if (!StringUtils.isEmpty(params.getOrganizationId())) {
                    Predicate organizationId = criteriaBuilder.equal(root.get("organizationId"), params.getOrganizationId());
                    predicates.add(organizationId);
                }
            } else {
                // 只查询当前机构的订单
                Predicate organizationId = criteriaBuilder.equal(root.get("organizationId"), params.getOrganizationId());
                predicates.add(organizationId);
                // 只能看到自己的订单
                Predicate userId = criteriaBuilder.equal(root.get("userId"), params.getUserId());
                predicates.add(userId);
            }
            if (!StringUtils.isEmpty(params.getInvoice())) {
                Predicate organizationId = criteriaBuilder.equal(root.get("invoice").as(String.class), params.getInvoice());
                predicates.add(organizationId);
            }
            if (!StringUtils.isEmpty(params.getSettlement())) {
                Predicate organizationId = criteriaBuilder.equal(root.get("settlement").as(String.class), params.getSettlement());
                predicates.add(organizationId);
            }
            if (!StringUtils.isEmpty(params.getOutInvoice())) {
                Predicate organizationId = criteriaBuilder.equal(root.get("outInvoice").as(String.class), params.getOutInvoice());
                predicates.add(organizationId);
            }
            if (!StringUtils.isEmpty(params.getOutSettlement())) {
                Predicate organizationId = criteriaBuilder.equal(root.get("outSettlement").as(String.class), params.getOutSettlement());
                predicates.add(organizationId);
            }
            if (StringUtils.hasText(params.getDaiBan())) {
                //封装or语句
                List<Predicate> listOr = new ArrayList<>();
                if (TypeStringUtils.feeInType.equals(params.getType())) {
                    Predicate invoice = criteriaBuilder.equal(root.get("invoice").as(String.class), BillType.STATUS_WKP);
                    listOr.add(invoice);
                    Predicate settlement = criteriaBuilder.equal(root.get("settlement").as(String.class), BillType.STATUS_WJS);
                    listOr.add(settlement);
                } else {
                    Predicate outInvoice = criteriaBuilder.equal(root.get("outInvoice").as(String.class), BillType.STATUS_WKP);
                    listOr.add(outInvoice);
                    Predicate outSettlement = criteriaBuilder.equal(root.get("outSettlement").as(String.class), BillType.STATUS_WJS);
                    listOr.add(outSettlement);
                }
                Predicate[] arrayOr = new Predicate[listOr.size()];
                Predicate pre_Or = criteriaBuilder.or(listOr.toArray(arrayOr));
                predicates.add(pre_Or);
            }
            if (StringUtils.hasText(params.getOrderDaiBan())) {
                Predicate alert = criteriaBuilder.equal(root.get("alert"), Boolean.TRUE);
                predicates.add(alert);
            }
            if (!StringUtils.isEmpty(params.getClientId())) {
                Predicate code = criteriaBuilder.equal(root.get("clientId").as(String.class), params.getClientId());
                predicates.add(code);
            }
            if (!StringUtils.isEmpty(params.getCarrierId())) {
                Carrier carrier = carrierRepository.findById(params.getCarrierId());
                Predicate carrierId = criteriaBuilder.like(root.get("carrierName").as(String.class), "%" + carrier.getName() + "%");
                predicates.add(carrierId);
            }
            if (!StringUtils.isEmpty(params.getBookName())) {
                Predicate bookName = criteriaBuilder.like(root.get("bookName").as(String.class), "%" + params.getBookName().trim() + "%");
                predicates.add(bookName);
            }
            if (!StringUtils.isEmpty(params.getOriginPlace())) {
                Predicate originPlace = criteriaBuilder.like(root.get("originPlace").as(String.class), "%" + params.getOriginPlace().trim() + "%");
                predicates.add(originPlace);
            }
            if (!StringUtils.isEmpty(params.getCarrierDeparturePort())) {
                Predicate carrierDeparturePort = criteriaBuilder.like(root.get("carrierDeparturePort").as(String.class), "%" + params.getCarrierDeparturePort().trim() + "%");
                predicates.add(carrierDeparturePort);
            }
            if (!StringUtils.isEmpty(params.getDestPlace())) {
                Predicate destPlace = criteriaBuilder.like(root.get("destPlace").as(String.class), "%" + params.getDestPlace().trim() + "%");
                predicates.add(destPlace);
            }
            if (!StringUtils.isEmpty(params.getUserName())) {
                Predicate userName = criteriaBuilder.like(root.get("userName").as(String.class), "%" + params.getUserName().trim() + "%");
                predicates.add(userName);
            }
            if (!StringUtils.isEmpty(params.getCabinetNumber())) {
                Predicate cabinetNumberArr = criteriaBuilder.like(root.get("cabinetNumber").as(String.class), "%" + params.getCabinetNumber().trim() + "%");
                predicates.add(cabinetNumberArr);
            }
            if (!StringUtils.isEmpty(params.getOrderNumber())) {
                Predicate orderNumber = criteriaBuilder.like(root.get("orderNumber").as(String.class), "%" + params.getOrderNumber().trim() + "%");
                predicates.add(orderNumber);
            }
            if (!StringUtils.isEmpty(params.getBusinessNumber())) {
                Predicate businessNumber = criteriaBuilder.like(root.get("businessNumber").as(String.class), "%" + params.getBusinessNumber().trim() + "%");
                predicates.add(businessNumber);
            }
            if (!StringUtils.isEmpty(params.getBookNumber())) {
                Predicate bookNumber = criteriaBuilder.like(root.get("bookNumber").as(String.class), "%" + params.getBookNumber().trim() + "%");
                predicates.add(bookNumber);
            }
            if (params.getPickDateStart() != null) {
                Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("pickDate"), params.getPickDateStart());
                predicates.add(timeStart);
            }
            if (params.getPickDateEnd() != null) {
                Predicate timeEnd = criteriaBuilder.lessThan(root.get("pickDate"), params.getPickDateEnd().plusDays(1));
                predicates.add(timeEnd);
            }
            if (params.getPreDepartureTimeStart() != null) {
                Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("preDepartureTime"), params.getPreDepartureTimeStart());
                predicates.add(timeStart);
            }
            if (params.getPreDepartureTimeEnd() != null) {
                Predicate timeEnd = criteriaBuilder.lessThan(root.get("preDepartureTime"), params.getPreDepartureTimeEnd().plusDays(1));
                predicates.add(timeEnd);
            }
            if (params.getCreateTimeStart() != null) {
                Predicate createTimeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), params.getCreateTimeStart().atStartOfDay());
                predicates.add(createTimeStart);
            }
            if (params.getCreateTimeEnd() != null) {
                Predicate createTimeEnd = criteriaBuilder.lessThan(root.get("createTime"), params.getCreateTimeEnd().plusDays(1).atStartOfDay());
                predicates.add(createTimeEnd);
            }
            if (!StringUtils.isEmpty(params.getAlert())) {
                Predicate alert = criteriaBuilder.equal(root.get("alert"), params.getAlert());
                predicates.add(alert);
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort order = new Sort(Sort.Direction.DESC, "createTime", "orderNumber");
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(specification, order);
        List<String> ids = zadOrders.stream().map(ZadOrder::getId).collect(Collectors.toList());
        List<OrderFollow> follows = orderFollowRepository.findByOrderIdIn(ids);
        List<ZadOrderDto> dtos = new ArrayList<>();
        for (ZadOrder zadOrder : zadOrders) {
            ZadOrderDto dto = new ZadOrderDto();
            BeanUtils.copyProperties(zadOrder, dto);
            List<OrderFollow> orderFollows = follows.stream().filter(follow -> follow.getOrderId().equals(zadOrder.getId())).collect(Collectors.toList());
            if (orderFollows.size() == 0) {
                OrderFollowDto followDto = new OrderFollowDto();
                dto.getFollowDtos().add(followDto);
            } else {
                orderFollows.forEach(follow -> {
                    OrderFollowDto followDto = new OrderFollowDto();
                    BeanUtils.copyProperties(follow, followDto);
                    dto.getFollowDtos().add(followDto);
                });
            }
            dtos.add(dto);
        }
        return dtos;
    }

    @Override
    public List<OrderFollowDto> getFollowsById(String id) {
        ZadOrder zadOrder = zadOrderRepository.findById(id);
        List<OrderFollowDto> followDtos = new ArrayList<>();
        List<OrderFollow> follows = orderFollowRepository.findByOrderId(id);
        if (follows.size() == 0) {
            OrderFollowDto dto = new OrderFollowDto();
            dto.setCabinetNumber(zadOrder.getCabinetNumber());
            followDtos.add(dto);
        } else {
            OrderFollowDto dto = new OrderFollowDto();
            BeanUtils.copyProperties(follows.get(0), dto);
            followDtos.add(dto);
        }
        return followDtos;
    }

    @Override
    public void confirmOrder(List<String> ids, String userId) {
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(ids);
        User user = userRepository.findById(userId);
        List<LogCreateDto> dtos = new ArrayList<>();
        zadOrders.forEach(order -> {
            order.setStatus(OrderStatus.CONFIRMED);
            LogCreateDto logCreateDto = new LogCreateDto(user.getId(), user.getNickName(), "锁定中欧/东南亚班列", order.getOrderNumber());
            dtos.add(logCreateDto);
        });
        zadOrderRepository.save(zadOrders);
        /*// 锁定所有费用
        List<OrderFee> fees = orderFeeRepository.findByOrderIdIn(ids);
        for (OrderFee fee : fees) {
            fee.setStatus(TypeStringUtils.feeLockStatus);
        }
        orderFeeRepository.save(fees);*/
        dtos.forEach(logService::create);
    }

    @Override
    public void unConfirmOrder(List<String> ids, String userId) {
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(ids);
        User user = userRepository.findById(userId);
        List<LogCreateDto> dtos = new ArrayList<>();
        zadOrders.forEach(order -> {
            order.setStatus(OrderStatus.UN_CONFIRM);
            LogCreateDto logCreateDto = new LogCreateDto(user.getId(), user.getNickName(), "解锁中欧/东南亚班列", order.getOrderNumber());
            dtos.add(logCreateDto);
        });
        zadOrderRepository.save(zadOrders);
        /*// 解锁所有费用
        List<OrderFee> fees = orderFeeRepository.findByOrderIdIn(ids);
        for (OrderFee fee : fees) {
            fee.setStatus(TypeStringUtils.feeUnLockStatus);
        }
        orderFeeRepository.save(fees);*/
        dtos.forEach(logService::create);
    }

    @Override
    public List<ZadOrderDto> query(String orgId, String selectId, String createDateStart, String createDateEnd, String currency, String type, List<ExchangeRate> finalRates, String userId) {
        User user = userRepository.findById(userId);
        Role role = roleRepository.findById(user.getRoleId());
        Specification<ZadOrder> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            if (RoleType.SUPER_ADMIN.equals(role.getName())) {
                // 得到所有的订单
                List<String> orgIds = organizationService.getAllOrgIds();
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
                // 组织机构ID
                if (StringUtils.hasText(orgId)) {
                    Predicate organizationId = criteriaBuilder.equal(root.get("organizationId"), orgId);
                    predicates.add(organizationId);
                }
            } else if (RoleType.ADMIN.equals(role.getName())) {
                // 得到当前机构及子机构的所有订单
                List<String> orgIds = organizationService.getChildOrgIds(user.getOrganizationId());
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
                // 组织机构ID
                if (StringUtils.hasText(orgId)) {
                    Predicate organizationId = criteriaBuilder.equal(root.get("organizationId"), orgId);
                    predicates.add(organizationId);
                }
            } else if (role.isEqualAdmin()) {
                // 授予了机构管理员权限
                List<String> orgIds = organizationService.getChildOrgIds(user.getOrganizationId());
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
                // 组织机构ID
                if (StringUtils.hasText(orgId)) {
                    Predicate organizationId = criteriaBuilder.equal(root.get("organizationId"), orgId);
                    predicates.add(organizationId);
                }
            } else {
                // 得到属于用户当前机构的订单
                List<String> orgIds = organizationService.getChildOrgIds(user.getOrganizationId());
                Expression<String> exp = root.get("organizationId");
                predicates.add(exp.in(orgIds));
            }
            if (TypeStringUtils.feeInType.equals(type) && StringUtils.hasText(selectId)) {
                Predicate clientIdEqual = criteriaBuilder.equal(root.get("clientId").as(String.class), selectId);
                predicates.add(clientIdEqual);
            } else if (TypeStringUtils.feeOutType.equals(type) && StringUtils.hasText(selectId)) {
                Carrier carrier = carrierRepository.findById(selectId);
                Predicate carrierId = criteriaBuilder.like(root.get("carrierName").as(String.class), "%" + carrier.getName() + "%");
                predicates.add(carrierId);
            }
            if (StringUtils.hasText(createDateStart)) {
                Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("preDepartureTime"), LocalDate.parse(createDateStart, dateTimeFormatter));
                predicates.add(timeStart);
            }
            if (StringUtils.hasText(createDateEnd)) {
                Predicate timeEnd = criteriaBuilder.lessThan(root.get("preDepartureTime"), LocalDate.parse(createDateEnd, dateTimeFormatter).plusDays(1));
                predicates.add(timeEnd);
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort order = new Sort(Sort.Direction.DESC, "createTime", "orderNumber");
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(specification, order);
        List<String> ids = zadOrders.stream().map(ZadOrder::getId).collect(Collectors.toList());
        List<OrderFee> fees = orderFeeRepository.findByOrderIdIn(ids);
        List<ZadOrderDto> dtos = new ArrayList<>();
        Carrier carrier = carrierRepository.findById(selectId);
        zadOrders.forEach(zadOrder -> {
            ZadOrderDto dto = new ZadOrderDto();
            BeanUtils.copyProperties(zadOrder, dto);
            List<OrderFee> orderFees = fees.stream().filter(orderFee -> orderFee.getOrderId().equals(zadOrder.getId())).collect(Collectors.toList());
            if (orderFees.size() > 0) {
                List<FeeDto> feeDtos = new ArrayList<>();
                List<OrderFee> orderFeeList = orderFees.stream().filter(fee -> fee.getType().equals(type)).collect(Collectors.toList());
                // 如果是应付，只需要过滤出所选的供应商费用即可(注意：费用的供应商字段是供应商的简称)
                if (TypeStringUtils.feeOutType.equals(type) && StringUtils.hasText(selectId)) {
                    orderFeeList = orderFeeList.stream().filter(fee -> carrier.getName().equals(fee.getCarrierId())).collect(Collectors.toList());
                }
                orderFeeList.forEach(orderFee -> {
                    // 如果费用锁定了，也不导出
                    if (TypeStringUtils.feeLockStatus.equals(orderFee.getStatus())) {
                        return;
                    }
                    FeeDto feeDto = new FeeDto();
                    BeanUtils.copyProperties(orderFee, feeDto);
                    if (StringUtils.isEmpty(currency) || TypeStringUtils.unTransfer.equals(currency) || currency.equals(feeDto.getCurrency())) {
                        feeDtos.add(feeDto);
                    } else {
                        double value = OrderAnalysisServiceImpl.getValueFromCurrency(finalRates, orderFee.getCurrency(), orderFee.getFeeCount());
                        feeDto.setFeeCount(value);
                        feeDtos.add(feeDto);
                    }
                });
                dto.setFeeList(feeDtos);
            }
            dtos.add(dto);
        });
        return dtos;
    }

    @Override
    public List<ZadOrderDto> query(QueryDto queryDto) {
        ZadOrderQueryDto zadOrderQueryDto = new ZadOrderQueryDto();
        BeanUtils.copyProperties(queryDto, zadOrderQueryDto);
        List<ZadOrderDto> zadOrderDtos = this.queryAll(zadOrderQueryDto);
        List<Carrier> carriers = carrierRepository.findAll();
        List<OrderFee> fees = orderFeeRepository.findByOrderIdIn(zadOrderDtos.stream().map(ZadOrderDto::getId).collect(Collectors.toList()));
        zadOrderDtos.forEach(dto -> {
            List<OrderFee> orderFees = fees.stream().filter(orderFee -> orderFee.getOrderId().equals(dto.getId())).collect(Collectors.toList());
            if (orderFees.size() > 0) {
                List<FeeDto> feeDtos = new ArrayList<>();
                List<OrderFee> orderFeeList = orderFees.stream().filter(fee -> fee.getType().equals(queryDto.getType())).collect(Collectors.toList());
                // 如果是应付，只需要过滤出所选的供应商费用即可(注意：费用的供应商字段是供应商的简称)
                if (TypeStringUtils.feeOutType.equals(queryDto.getType()) && StringUtils.hasText(queryDto.getCarrierId())) {
                    Carrier carrier = carriers.stream().filter(ca -> ca.getId().equals(queryDto.getCarrierId())).findFirst().orElse(null);
                    if (carrier != null) {
                        orderFeeList = orderFeeList.stream().filter(fee -> carrier.getName().equals(fee.getCarrierId())).collect(Collectors.toList());
                    }
                }
                orderFeeList.forEach(orderFee -> {
                    // 如果费用锁定了，也不导出
                    if (TypeStringUtils.feeLockStatus.equals(orderFee.getStatus())) {
                        return;
                    }
                    FeeDto feeDto = new FeeDto();
                    BeanUtils.copyProperties(orderFee, feeDto);
                    if (StringUtils.isEmpty(queryDto.getCurrency()) || TypeStringUtils.unTransfer.equals(queryDto.getCurrency()) || queryDto.getCurrency().equals(feeDto.getCurrency())) {
                        feeDtos.add(feeDto);
                    } else {
                        double value = OrderAnalysisServiceImpl.getValueFromCurrency(queryDto.getRateList(), orderFee.getCurrency(), orderFee.getFeeCount());
                        // 全部转成对应币种
                        feeDto.setCurrency(queryDto.getCurrency());
                        feeDto.setFeeCount(value);
                        feeDtos.add(feeDto);
                    }
                });
                dto.setFeeList(feeDtos);
            }
        });
        return zadOrderDtos;
    }

    @Override
    public List<ZadOrderDto> getAllByIds(List<String> ids) {
        Specification<ZadOrder> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("id");
            predicates.add(exp.in(ids));
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort order = new Sort(Sort.Direction.DESC, "createTime", "orderNumber");
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(specification, order);
        List<OrderFollow> follows = orderFollowRepository.findByOrderIdIn(ids);
        List<ZadOrderDto> dtos = new ArrayList<>();
        for (ZadOrder zadOrder : zadOrders) {
            ZadOrderDto dto = new ZadOrderDto();
            BeanUtils.copyProperties(zadOrder, dto);
            List<OrderFollow> orderFollows = follows.stream().filter(follow -> follow.getOrderId().equals(zadOrder.getId())).collect(Collectors.toList());
            if (orderFollows.size() == 0) {
                OrderFollowDto followDto = new OrderFollowDto();
                dto.getFollowDtos().add(followDto);
            } else {
                orderFollows.forEach(follow -> {
                    OrderFollowDto followDto = new OrderFollowDto();
                    BeanUtils.copyProperties(follow, followDto);
                    dto.getFollowDtos().add(followDto);
                });
            }
            dtos.add(dto);
        }
        return dtos;
    }

    @Override
    public List<ZadOrderDto> getAllByIds(List<String> ids, String type, String currencyType, List<ExchangeRate> finalRateList) {
        Specification<ZadOrder> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("id");
            predicates.add(exp.in(ids));
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort order = new Sort(Sort.Direction.DESC, "createTime", "orderNumber");
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(specification, order);
        List<OrderFee> fees = orderFeeRepository.findByOrderIdIn(ids);
        List<ZadOrderDto> dtos = new ArrayList<>();
        zadOrders.forEach(zadOrder -> {
            ZadOrderDto dto = new ZadOrderDto();
            BeanUtils.copyProperties(zadOrder, dto);
            List<OrderFee> orderFees = fees.stream().filter(orderFee -> orderFee.getOrderId().equals(zadOrder.getId())).collect(Collectors.toList());
            if (orderFees.size() > 0) {
                List<FeeDto> feeDtos = new ArrayList<>();
                orderFees.forEach(orderFee -> {
                    if (orderFee.getFeeCount() == 0) {
                        return;
                    }
                    // 如果费用锁定了，也不导出
                    if (TypeStringUtils.feeLockStatus.equals(orderFee.getStatus())) {
                        return;
                    }
                    if (type.equals(orderFee.getType())) {
                        FeeDto feeDto = new FeeDto();
                        BeanUtils.copyProperties(orderFee, feeDto);
                        if (StringUtils.isEmpty(currencyType) || TypeStringUtils.unTransfer.equals(currencyType) || currencyType.equals(feeDto.getCurrency())) {
                            feeDtos.add(feeDto);
                        } else {
                            double value = OrderAnalysisServiceImpl.getValueFromCurrency(finalRateList, orderFee.getCurrency(), orderFee.getFeeCount());
                            // 全部转成对应币种
                            feeDto.setCurrency(currencyType);
                            feeDto.setFeeCount(value);
                            feeDtos.add(feeDto);
                        }

                    }
                });
                dto.setFeeList(feeDtos);
            }
            dtos.add(dto);
        });
        return dtos;
    }

    @Override
    public void lockFee(List<String> ids, Set<String> lockColumns) {
        List<OrderFee> orderFees = orderFeeRepository.findByOrderIdIn(ids);
        orderFees.forEach(orderFee -> {
            if (lockColumns.contains(orderFee.getFeeName())) {
                orderFee.setStatus(TypeStringUtils.feeLockStatus);
            }
        });
        orderFeeRepository.save(orderFees);
    }

    @Override
    public List<ZadOrder> findAlertList(String orgId) {
        List<String> childOrgIds = organizationService.getChildOrgIds(orgId);
        return zadOrderRepository.findByAlertIsTrueAndOrganizationIdIn(childOrgIds);
    }


    @Override
    public void changeInvoice(List<String> orderIds, String type, String opId, String remark) {
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(orderIds);
        User op = userRepository.findById(opId);
        for (ZadOrder zadOrder : zadOrders) {
            if (TypeStringUtils.feeInType.equals(type)) {
                zadOrder.setInvoice("已开票");
                LogCreateDto logCreateDto = new LogCreateDto(op.getId(), op.getNickName(), "班列订单应收已开票", zadOrder.getOrderNumber());
                logService.create(logCreateDto);
            } else {
                zadOrder.setOutInvoice("已开票");
                LogCreateDto logCreateDto = new LogCreateDto(op.getId(), op.getNickName(), "班列订单应付已开票", zadOrder.getOrderNumber());
                logService.create(logCreateDto);
            }
            zadOrder.setRemark(remark);
            zadOrder.checkCanLock();
            zadOrderRepository.save(zadOrder);
        }
    }

    @Override
    public void changeSettlement(List<String> orderIds, String type, String opId, String remark) {
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(orderIds);
        User op = userRepository.findById(opId);
        // 判断是否能封账，柜号是否有还柜日期
        for (ZadOrder zadOrder : zadOrders) {
            // 集装箱数据没有实际还箱日期，锁账需要弹窗提醒
            if (StringUtils.hasText(zadOrder.getCabinetNumber())) {
                ContainerOrder containerOrder = containerOrderRepository.findByOrderId(zadOrder.getId());
                Assert.isTrue(containerOrder == null || containerOrder.getActReturnDate() != null, String.format("关联的集装箱【%s】没有实际还箱日期，不能封账", zadOrder.getCabinetNumber()));
            }
        }
        for (ZadOrder zadOrder : zadOrders) {
            zadOrder.setSettlement(BillType.STATUS_YJS);
            LogCreateDto logCreateDto = new LogCreateDto(op.getId(), op.getNickName(), "班列订单应收已结算", zadOrder.getOrderNumber());
            logService.create(logCreateDto);
            // 把所有应收的费用锁定
            List<OrderFee> fees = orderFeeRepository.findByOrderIdAndType(zadOrder.getId(), TypeStringUtils.feeInType);
            for (OrderFee fee : fees) {
                fee.setStatus(TypeStringUtils.feeLockStatus);
            }
            orderFeeRepository.save(fees);
            zadOrder.setOutSettlement(BillType.STATUS_YJS);
            logCreateDto = new LogCreateDto(op.getId(), op.getNickName(), "班列订单应付已结算", zadOrder.getOrderNumber());
            logService.create(logCreateDto);
            // 把所有应付的费用锁定
            fees = orderFeeRepository.findByOrderIdAndType(zadOrder.getId(), TypeStringUtils.feeOutType);
            for (OrderFee fee : fees) {
                fee.setStatus(TypeStringUtils.feeLockStatus);
            }
            orderFeeRepository.save(fees);
            zadOrder.setRemark(remark);
            zadOrder.checkCanLock();
        }
        zadOrderRepository.save(zadOrders);
    }

    @Override
    public void invoiceOrder(OperateOrderDto operateOrderDto) {
        ExportLog exportLog = exportLogRepository.findFirstById(operateOrderDto.getLogId());
        if (operateOrderDto.getInvoiceDate() != null) {
            exportLog.setInvoiceDate(operateOrderDto.getInvoiceDate());
        }
        if (operateOrderDto.getSettlementDate() != null) {
            exportLog.setSettlementDate(operateOrderDto.getSettlementDate());
        }
        exportLog.setSettlementDate(operateOrderDto.getSettlementDate());
        if (StringUtils.hasText(operateOrderDto.getRemark())) {
            String text = StringUtils.isEmpty(exportLog.getRemark()) ? "" : exportLog.getRemark() + ";";
            exportLog.setRemark(text + operateOrderDto.getRemark());
        }
        exportLogRepository.save(exportLog);
        String ids = exportLog.getIds();
        if (ids != null) {
            String[] idsArray = ids.split(",");
            this.changeInvoice(Arrays.asList(idsArray), operateOrderDto.getType(), operateOrderDto.getOpId(), operateOrderDto.getRemark());
        }
    }

    @Override
    public void settlementOrder(OperateOrderDto operateOrderDto) {
        ExportLog exportLog = exportLogRepository.findFirstById(operateOrderDto.getLogId());
        if (operateOrderDto.getInvoiceDate() != null) {
            exportLog.setInvoiceDate(operateOrderDto.getInvoiceDate());
        }
        if (operateOrderDto.getSettlementDate() != null) {
            exportLog.setSettlementDate(operateOrderDto.getSettlementDate());
        }
        exportLog.setSettlementDate(operateOrderDto.getSettlementDate());
        if (StringUtils.hasText(operateOrderDto.getRemark())) {
            String text = StringUtils.isEmpty(exportLog.getRemark()) ? "" : exportLog.getRemark() + ";";
            exportLog.setRemark(text + operateOrderDto.getRemark());
        }
        exportLogRepository.save(exportLog);
        String ids = exportLog.getIds();
        if (ids != null) {
            String[] idsArray = ids.split(",");
            this.changeSettlement(Arrays.asList(idsArray), operateOrderDto.getType(), operateOrderDto.getOpId(), operateOrderDto.getRemark());
        }
    }

    @Override
    public Result<?> batchCreate(List<ZadOrderImportDto> importDatas, String opId) {
        User user = userRepository.findById(opId);
        List<Organization> organizations = organizationService.getAll();
        List<Client> clients = clientRepository.findAll();
        // 录入失败的柜号信息列表
        List<String> messageList = new ArrayList<>();
        List<ZadImportCheckVo> zadImportCheckVos = new ArrayList<>();
        ZadImportCheckVo checkVo;
        for (ZadOrderImportDto importData : importDatas) {
            if (!StringUtils.hasText(importData.getCabinetNumber())) {
                continue;
            }
            checkVo = zadImportCheckVos.stream().filter(item -> Objects.equals(item.getOrgName(), importData.getOrganizationName()) && Objects.equals(item.getPackageNo(), importData.getCabinetNumber())).findFirst().orElse(null);
            if (Objects.isNull(checkVo)) {
                checkVo = new ZadImportCheckVo();
                checkVo.setOrgName(importData.getOrganizationName());
                checkVo.setPackageNo(importData.getCabinetNumber());
                zadImportCheckVos.add(checkVo);
            } else {
                checkVo.setCount(checkVo.getCount() + 1);
            }
        }
        zadImportCheckVos.forEach(item -> {
            if (item.getCount() > 1) {
                messageList.add(String.format("导入表格有错误：机构【%s】，柜号【%s】重复，请去掉重复再进行导入", item.getOrgName(), item.getPackageNo()));
            }
        });
        // 每一行检查的报错信息列表
        List<String> lineErrorList;
        // 允许导入的数据
        List<ZadOrderImportDto> canImportDtos = new ArrayList<>();
        int currentIndex = 1;
        for (ZadOrderImportDto importData : importDatas) {
            lineErrorList = new ArrayList<>();
            if (StringUtils.isEmpty(importData.getOrganizationName())) {
                lineErrorList.add(String.format("第【%s】行数据，机构抬头不能为空", currentIndex));
            } else {
                boolean anyMatch = organizations.stream().anyMatch(organization -> organization.getName().equals(importData.getOrganizationName()));
                if (!anyMatch) {
                    lineErrorList.add(String.format("第【%s】行数据，没有找到机构抬头为【%s】的记录", currentIndex, importData.getOrganizationName()));
                }
            }
            if (StringUtils.isEmpty(importData.getClientName())) {
                lineErrorList.add(String.format("第【%s】行数据，客户简称不能为空", currentIndex));
            } else {
                // 再判断当前所选机构是否有此客户
                Optional<Organization> optional = organizations.stream().filter(organization -> organization.getName().equals(importData.getOrganizationName())).findFirst();
                if (optional.isPresent()) {
                    Organization organization = optional.get();
                    List<Client> filterClients = clients.stream().filter(client -> client.getOrganizationId().equals(organization.getId())).collect(Collectors.toList());
                    boolean anyMatch = filterClients.stream().anyMatch(client -> client.getName().equals(importData.getClientName()));
                    if (!anyMatch) {
                        lineErrorList.add(String.format("第【%s】行数据，在机构【%s】下找不到客户简称为【%s】的客户", currentIndex, organization.getName(), importData.getClientName()));
                    }
                }
            }
            boolean anyMatch = clients.stream().anyMatch(client -> client.getName().equals(importData.getClientName()));
            if (!anyMatch) {
                lineErrorList.add(String.format("第【%s】行数据，没有找到客户简称为【%s】的记录", currentIndex, importData.getClientName()));
            }
            if (StringUtils.isEmpty(importData.getPreDepartureTime())) {
                lineErrorList.add(String.format("第【%s】行数据，班列日期必填", currentIndex));
            }
            if (StringUtils.isEmpty(importData.getCalculateType())) {
                lineErrorList.add(String.format("第【%s】行数据，计费方式必填", currentIndex));
            } else {
                CalculateType calculateType = CalculateTypeUtil.getCalculateType(importData.getCalculateType());
                if (calculateType == null) {
                    lineErrorList.add(String.format("第【%s】行数据，系统不存在此计费方式【%s】", currentIndex, importData.getCalculateType()));
                } else {
                    // 按照数量计费，但是数量没值
                    if (calculateType.equals(CalculateType.QUANTITY) && StringUtils.isEmpty(importData.getQuantity())) {
                        lineErrorList.add(String.format("第【%s】行数据，计费方式为数量，但【数量】一列没有数据", currentIndex));
                    } else if (calculateType.equals(CalculateType.VOLUME) && StringUtils.isEmpty(importData.getVolume())) {
                        lineErrorList.add(String.format("第【%s】行数据，计费方式为体积，但【货物体积(m³)】一列没有数据", currentIndex));
                    } else if (calculateType.equals(CalculateType.WEIGHT) && StringUtils.isEmpty(importData.getGoodsWeight())) {
                        lineErrorList.add(String.format("第【%s】行数据，计费方式为重量，但【货物重量(KG)】一列没有数据", currentIndex));
                    }
                }
            }
            if (StringUtils.isEmpty(importData.getBusinessCode())) {
                lineErrorList.add(String.format("第【%s】行数据，业务类型必填", currentIndex));
            }
            /*if (StringUtils.isEmpty(importData.getBookName())) {
                lineErrorList.add(String.format("第【%s】行数据，订舱人必填", currentIndex));
            }
            if (StringUtils.isEmpty(importData.getBookNumber())) {
                lineErrorList.add(String.format("第【%s】行数据，订舱号必填", currentIndex));
            }*/
            if (StringUtils.isEmpty(importData.getOriginPlace())) {
                lineErrorList.add(String.format("第【%s】行数据，起始站必填", currentIndex));
            }
            if (StringUtils.isEmpty(importData.getDestPlace())) {
                lineErrorList.add(String.format("第【%s】行数据，目的站必填", currentIndex));
            }
            if (lineErrorList.size() == 0) {
                canImportDtos.add(importData);
            } else {
                messageList.add(String.join(";", lineErrorList));
            }
            // 下一行
            currentIndex++;
        }
        if (messageList.size() > 0) {
            return Result.success(messageList);
        }
        // 检查导入文件是否存在相同柜号，
        List<String> orgNames = importDatas.stream().map(ZadOrderImportDto::getOrganizationName).distinct().collect(Collectors.toList());
        // 查出所有符合条件的班列记录
        List<ZadOrder> zadOrders = zadOrderRepository.findByOrganizationNameIn(orgNames);
        for (ZadOrderImportDto importData : canImportDtos) {
            if (StringUtils.hasText(importData.getOrderNumber())) {
                // 订单编号、机构、客户、柜号、订舱号、订舱人、业务类型相同时，其他部分直接覆盖
                boolean hasRecord = zadOrders.stream().anyMatch(order -> Objects.equals(order.getOrderNumber(), importData.getOrderNumber()) &&
                        Objects.equals(order.getOrganizationName(), importData.getOrganizationName()) &&
                        Objects.equals(order.getClientName(), importData.getClientName()) &&
                        Objects.equals(order.getBusinessCode(), importData.getBusinessCode()) &&
                        StringUtils.hasText(order.getCabinetNumber()) &&
                        Objects.equals(order.getCabinetNumber(), importData.getCabinetNumber()) &&
                        Objects.equals(order.getBookName(), importData.getBookName()) &&
                        Objects.equals(order.getBookNumber(), importData.getBookNumber()));
                if (hasRecord) {
                    messageList.add(String.format("同一机构【%s】柜号【%s】重复", importData.getOrganizationName(), importData.getCabinetNumber()));
                    continue;
                } else {
                    // 导入时订单编号一样，机构、客户、柜号、订舱号、订舱人、业务类型相同时，其他部分直接覆盖
                    hasRecord = zadOrders.stream().anyMatch(order -> !Objects.equals(order.getOrderNumber(), importData.getOrderNumber()) &&
                            Objects.equals(order.getOrganizationName(), importData.getOrganizationName()) &&
                            Objects.equals(order.getClientName(), importData.getClientName()) &&
                            Objects.equals(order.getBusinessCode(), importData.getBusinessCode()) &&
                            StringUtils.hasText(order.getCabinetNumber()) &&
                            Objects.equals(order.getCabinetNumber(), importData.getCabinetNumber()) &&
                            Objects.equals(order.getBookName(), importData.getBookName()) &&
                            Objects.equals(order.getBookNumber(), importData.getBookNumber()));
                    if (hasRecord) {
                        messageList.add(String.format("同一机构【%s】柜号【%s】重复", importData.getOrganizationName(), importData.getCabinetNumber()));
                        continue;
                    }
                }
            } else {
                // 机构、客户、柜号、订舱号、订舱人、业务类型相同时，其他部分直接覆盖
                boolean hasRecord = zadOrders.stream().anyMatch(order -> Objects.equals(order.getOrganizationName(), importData.getOrganizationName()) &&
                        Objects.equals(order.getClientName(), importData.getClientName()) &&
                        Objects.equals(order.getBusinessCode(), importData.getBusinessCode()) &&
                        StringUtils.hasText(order.getCabinetNumber()) &&
                        Objects.equals(order.getCabinetNumber(), importData.getCabinetNumber()) &&
                        Objects.equals(order.getBookName(), importData.getBookName()) &&
                        Objects.equals(order.getBookNumber(), importData.getBookNumber()));
                if (hasRecord) {
                    messageList.add(String.format("同一机构【%s】柜号【%s】重复", importData.getOrganizationName(), importData.getCabinetNumber()));
                    continue;
                }
            }
            ZadOrderCreateDto createDto = new ZadOrderCreateDto();
            if (importData.getQuantity() == null) {
                importData.setQuantity(1);
            }
            if (importData.getVolume() == null) {
                importData.setVolume(0d);
            }
            if (importData.getGoodsWeight() == null) {
                importData.setGoodsWeight(0d);
            }
            BeanUtils.copyProperties(importData, createDto);
            if (StringUtils.hasText(importData.getCalculateType())) {
                CalculateType calculateType = CalculateTypeUtil.getCalculateType(importData.getCalculateType());
                if (calculateType != null) {
                    createDto.setCalculateType(calculateType.name());
                }
            }
            if (StringUtils.hasText(importData.getPreDepartureTime())) {
                Instant instant = DateUtils.parseDate(importData.getPreDepartureTime()).toInstant();
                ZoneId zoneId = ZoneId.systemDefault();
                // atZone()方法返回在指定时区从此Instant生成的ZonedDateTime
                createDto.setPreDepartureTime(instant.atZone(zoneId).toLocalDate());
            }
            if (StringUtils.hasText(importData.getActDepartureTime())) {
                Instant instant = DateUtils.parseDate(importData.getActDepartureTime()).toInstant();
                ZoneId zoneId = ZoneId.systemDefault();
                // atZone()方法返回在指定时区从此Instant生成的ZonedDateTime
                createDto.setActDepartureTime(instant.atZone(zoneId).toLocalDate());
            }
            if (StringUtils.hasText(importData.getOrderArriveTime())) {
                Instant instant = DateUtils.parseDate(importData.getOrderArriveTime()).toInstant();
                ZoneId zoneId = ZoneId.systemDefault();
                // atZone()方法返回在指定时区从此Instant生成的ZonedDateTime
                createDto.setOrderArriveTime(instant.atZone(zoneId).toLocalDate().atStartOfDay());
            }
            if (StringUtils.hasText(importData.getOrderLeaveTime())) {
                Instant instant = DateUtils.parseDate(importData.getOrderLeaveTime()).toInstant();
                ZoneId zoneId = ZoneId.systemDefault();
                // atZone()方法返回在指定时区从此Instant生成的ZonedDateTime
                createDto.setOrderLeaveTime(instant.atZone(zoneId).toLocalDate().atStartOfDay());
            }
            organizations.stream().filter(organization -> Objects.equals(organization.getName(), importData.getOrganizationName())).findFirst().ifPresent(organization -> createDto.setOrganizationId(organization.getId()));
            if (StringUtils.isEmpty(createDto.getOrganizationId())) {
                createDto.setOrganizationId(user.getOrganizationId());

            }
            clients.stream().filter(client -> Objects.equals(client.getOrganizationId(), createDto.getOrganizationId())).collect(Collectors.toList()).stream().filter(client -> client.getName().equals(importData.getClientName())).findFirst().ifPresent(client -> createDto.setClientId(client.getId()));
            createDto.setOpId(opId);
            // 算费用
            CalculateDto calculateDto = new CalculateDto();
            BeanUtils.copyProperties(createDto, calculateDto);
            calculateDto.setCalculateType(CalculateTypeUtil.getCalculateType(importData.getCalculateType()));
            calculateDto.setCalculateTime(createDto.getPreDepartureTime());
            // 国际计算字段以班列日期为基础
            calculateDto.setType(1);
            if (importData.getGoodsWeight() != null) {
                calculateDto.setGrossWeight(importData.getGoodsWeight());
            }
            List<PriceDto> priceDtos = calculateService.calculate(calculateDto);
            if (priceDtos != null && priceDtos.size() > 0) {
                List<ZadOrderCreateDto.FeeDto> feeDtos = new ArrayList<>();
                for (PriceDto priceDto : priceDtos) {
                    ZadOrderCreateDto.FeeDto feeDto = new ZadOrderCreateDto.FeeDto();
                    BeanUtils.copyProperties(priceDto, feeDto);
                    feeDtos.add(feeDto);
                }
                createDto.setFeeList(feeDtos);
            }
            try {
                this.create(createDto);
            } catch (Exception exception) {
                messageList.add(String.format("机构【%s】柜号【%s】录入失败【%s】", importData.getOrganizationName(), importData.getCabinetNumber(), exception.getMessage()));
            }
        }
        return Result.success(messageList);
    }

    public static String getCurrency(String text) {
        if (text == null) return null;
        if (text.contains("$")) {
            return "美元";
        }
        return "人民币";
    }

    public static String getCurrencyV2(String text, List<String> currencyList) {
        if (text == null) return null;
        // 去除空格
        if (currencyList.stream().anyMatch(rate -> rate.contains(text))) {
            return text.trim();
        }
        if ("null".equalsIgnoreCase(text)) {
            return null;
        }
        return text;
    }

    public static String getMoney(String text) {
        if (text == null) return null;
        if (text.toLowerCase().contains("null")) {
            return null;
        }
        if (text.contains("$")) {
            return text.replace("$", "").trim();
        }
        if (text.contains("￥")) {
            return text.replace("￥", "").trim();
        }
        return text;
    }

    // 重新计算费用为0的应收应付
    @Override
    public void reCalculateFee(List<String> ids) {
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(ids);
        List<OrderFee> orderFees = orderFeeRepository.findByOrderIdIn(ids);
        CalculateDto calculateDto;
        for (ZadOrder zadOrder : zadOrders) {
            calculateDto = new CalculateDto();
            BeanUtils.copyProperties(zadOrder, calculateDto);
            calculateDto.setCalculateType(CalculateTypeUtil.getCalculateType(zadOrder.getCalculateType()));
            calculateDto.setNoTips(true);
            if (zadOrder.getGoodsWeight() != null) {
                calculateDto.setGrossWeight(zadOrder.getGoodsWeight());
            }
            // 重新计算应收应付费用
            List<PriceDto> inPriceList = calculateService.calculate(calculateDto);
            List<OrderFee> oFees = orderFees.stream().filter(orderFee -> orderFee.getOrderId().equals(zadOrder.getId())).collect(Collectors.toList());
            for (OrderFee oFee : oFees) {
                // 如果费用为0
                if (oFee.getFeeCount() == 0) {
                    // 判断费用类型
                    if (TypeStringUtils.feeInType.equals(oFee.getType()) && inPriceList != null && inPriceList.size() > 0) {
                        inPriceList.stream().filter(price -> price.getFeeName().equals(oFee.getFeeName())).findFirst().ifPresent(price -> {
                            oFee.setFeeCount(price.getFeeCount());
                            oFee.setCurrency(price.getCurrency());
                        });
                    } else if (TypeStringUtils.feeOutType.equals(oFee.getType())) {
                        // 先判断供应商是否不为空
                        if (StringUtils.hasText(oFee.getCarrierId())) {
                            calculateDto.setCarrierId(oFee.getCarrierId());
                            calculateDto.setFeeName(oFee.getFeeName());
                            List<PriceDto> outPriceList = calculateService.calculateOut(calculateDto);
                            if (outPriceList != null && outPriceList.size() > 0) {
                                oFee.setFeeCount(outPriceList.get(0).getFeeCount());
                                oFee.setCurrency(outPriceList.get(0).getCurrency());
                            }
                        }
                    }
                }
            }
            orderFeeRepository.save(oFees);
        }
    }

    @Override
    public void reCalculateFeeById(ReCalculateFeeParams params) {
        List<OrderFee> orderFees = orderFeeRepository.findByOrderId(params.getId());
        CalculateDto calculateDto = new CalculateDto();
        BeanUtils.copyProperties(params.getZadOrderDto(), calculateDto);
        calculateDto.setCalculateType(CalculateTypeUtil.getCalculateType(params.getZadOrderDto().getCalculateType()));
        // 国际班列按照班列日期计算费用
        calculateDto.setCalculateTime(params.getZadOrderDto().getPreDepartureTime());
        // 前端是否提示标识
        calculateDto.setNoTips(true);
        if (params.getZadOrderDto().getGoodsWeight() != null) {
            calculateDto.setGrossWeight(params.getZadOrderDto().getGoodsWeight());
        }
        // 国际班列标志
        calculateDto.setType(1);
        // 重新计算应收应付费用
        List<PriceDto> inPriceList = calculateService.calculate(calculateDto);
        for (OrderFee oFee : orderFees) {
            if (!oFee.getFeeName().equals(params.getFeeName())) continue;
            // 判断费用类型
            if (TypeStringUtils.feeInType.equals(oFee.getType()) && inPriceList != null && inPriceList.size() > 0) {
                inPriceList.stream().filter(price -> price.getFeeName().equals(oFee.getFeeName())).findFirst().ifPresent(price -> {
                    oFee.setFeeCount(price.getFeeCount());
                    oFee.setCurrency(price.getCurrency());
                });
            } else if (TypeStringUtils.feeOutType.equals(oFee.getType())) {
                // 先判断供应商是否不为空
                if (StringUtils.hasText(oFee.getCarrierId())) {
                    calculateDto.setCarrierId(oFee.getCarrierId());
                    calculateDto.setFeeName(oFee.getFeeName());
                    List<PriceDto> outPriceList = calculateService.calculateOut(calculateDto);
                    if (outPriceList != null && outPriceList.size() > 0) {
                        oFee.setFeeCount(outPriceList.get(0).getFeeCount());
                        oFee.setCurrency(outPriceList.get(0).getCurrency());
                    }
                }
            }
        }
        orderFeeRepository.save(orderFees);
    }

    @Override
    public ZadOrderDto getById(String id) {
        ZadOrder zadOrder = zadOrderRepository.findById(id);
        List<OrderFee> fees = orderFeeRepository.findByOrderId(id);
        List<Carrier> carriers = carrierRepository.findAll();
        ZadOrderDto dto = new ZadOrderDto();
        BeanUtils.copyProperties(zadOrder, dto);
        BaoGuanFee guanFee = baoGuanFeeRepository.findByBelongId(id);
        dto.setHomeFee(guanFee.getHomeFee());
        dto.setHomeCount(guanFee.getHomeCount());
        dto.setSheetFee(guanFee.getSheetFee());
        dto.setSheetCount(guanFee.getSheetCount());
        List<OrderFee> orderFees = fees.stream().filter(orderFee -> orderFee.getOrderId().equals(zadOrder.getId())).collect(Collectors.toList());
        if (orderFees.size() > 0) {
            List<FeeDto> feeDtos = new ArrayList<>();
            List<FeeDto> outFeeDtos = new ArrayList<>();
            orderFees.forEach(orderFee -> {
                FeeDto feeDto = new FeeDto();
                BeanUtils.copyProperties(orderFee, feeDto);
                if (TypeStringUtils.feeInType.equals(orderFee.getType())) {
                    feeDtos.add(feeDto);
                } else {
                    carriers.stream().filter(carrier -> carrier.getId().equals(feeDto.getCarrierId())).findFirst().ifPresent(carrier -> feeDto.setCarrierId(carrier.getName()));
                    outFeeDtos.add(feeDto);
                }
            });
            dto.setFeeList(feeDtos);
            dto.setOutFeeList(outFeeDtos);
        }
        return dto;
    }

    @Override
    public List<OrderFee> getFeeListById(String id) {
        return orderFeeRepository.findByOrderId(id);
    }

    @Override
    public List<OrderFee> getFeeListByIds(List<String> ids) {
        return orderFeeRepository.findByOrderIdIn(ids);
    }

    @Override
    public void batchImportFee(List<ZadOrderExportVo> importDatas, String opId) {
        List<String> orderNumbers =
                importDatas.stream().map(ZadOrderExportVo::getOrderNumber)
                        .filter(StringUtils::hasText).collect(Collectors.toList());
        List<ZadOrder> zadOrders = zadOrderRepository.findByOrderNumberIn(orderNumbers);
        List<OrderFee> orderFees = orderFeeRepository.findByOrderIdIn(zadOrders.stream().map(ZadOrder::getId).collect(Collectors.toList()));
        // 需要更新的订单
        List<ZadOrder> needUpdateOrders = new ArrayList<>();
        for (ZadOrderExportVo importData : importDatas) {
            if (StringUtils.isEmpty(importData.getOrderNumber())) {
                continue;
            }
            zadOrders.stream().filter(zadOrder -> zadOrder.getOrderNumber().equals(importData.getOrderNumber()))
                    .findFirst()
                    .ifPresent(zadOrder -> {
                        if (StringUtils.hasText(importData.getRemark())) {
                            zadOrder.setRemark(String.format("%s;%s", zadOrder.getRemark() == null ? "" :
                                            zadOrder.getRemark(),
                                    importData.getRemark()));
                            needUpdateOrders.add(zadOrder);
                        }
                        OrderFee fee;
                        if (importData.getTransportFee() != 0d) {
                            fee = orderFees.stream().filter(orderFee -> orderFee.getOrderId().equals(zadOrder.getId()) && TypeStringUtils.feeInType.equals(orderFee.getType()) && TypeStringUtils.TRANSPORT_FEE.equals(orderFee.getFeeName()))
                                    .findFirst().orElse(null);
                            if (fee != null) {
                                fee.setFeeCount(importData.getTransportFee());
                                fee.setCurrency(importData.getInCurrency());
                            } else {
                                fee = new OrderFee();
                                fee.setFeeName(TypeStringUtils.TRANSPORT_FEE);
                                fee.setFeeCount(importData.getTransportFee());
                                fee.setCurrency(importData.getInCurrency());
                                fee.setType(TypeStringUtils.feeInType);
                                fee.setOrderId(zadOrder.getId());
                                fee.setId(snowFlakeFactory.nextId("OF"));
                            }
                            orderFeeRepository.save(fee);
                        }
                        if (importData.getOutTransportFee() != 0d) {
                            fee = orderFees.stream().filter(orderFee -> orderFee.getOrderId().equals(zadOrder.getId()) && TypeStringUtils.feeOutType.equals(orderFee.getType()) && TypeStringUtils.TRANSPORT_FEE.equals(orderFee.getFeeName()))
                                    .findFirst().orElse(null);
                            if (fee != null) {
                                fee.setFeeCount(importData.getOutTransportFee());
                                fee.setCurrency(importData.getOutCurrency());
                                fee.setCarrierId(importData.getCarrier());
                                if (StringUtils.hasText(zadOrder.getCarrierName()) && !zadOrder.getCarrierName().contains(importData.getCarrier())) {
                                    zadOrder.setCarrierName(String.format("%s;%s", zadOrder.getCarrierName(), importData.getCarrier()));
                                } else {
                                    zadOrder.setCarrierName(importData.getCarrier());
                                }
                                needUpdateOrders.add(zadOrder);
                            } else {
                                fee = new OrderFee();
                                fee.setFeeName(TypeStringUtils.TRANSPORT_FEE);
                                fee.setFeeCount(importData.getOutTransportFee());
                                fee.setCurrency(importData.getOutCurrency());
                                fee.setType(TypeStringUtils.feeOutType);
                                fee.setOrderId(zadOrder.getId());
                                fee.setCarrierId(importData.getCarrier());
                                if (StringUtils.hasText(zadOrder.getCarrierName()) && !zadOrder.getCarrierName().contains(importData.getCarrier())) {
                                    zadOrder.setCarrierName(String.format("%s;%s", zadOrder.getCarrierName(), importData.getCarrier()));
                                } else {
                                    zadOrder.setCarrierName(importData.getCarrier());
                                }
                                needUpdateOrders.add(zadOrder);
                                fee.setId(snowFlakeFactory.nextId("OF"));
                            }
                            orderFeeRepository.save(fee);
                        }
                    });
        }
        if (needUpdateOrders.size() > 0) {
            zadOrderRepository.save(needUpdateOrders);
        }
    }

    @Override
    public Set<String> batchCreateInFee(List<Map<String, String>> dataList) {
        // 获取系统配置的币种列表
        List<ExchangeRate> exchangeRates = rateRepository.findAll();
        List<String> currencyList = new ArrayList<>();
        for (ExchangeRate exchangeRate : exchangeRates) {
            if (!currencyList.contains(exchangeRate.getFromRate())) {
                currencyList.add(exchangeRate.getFromRate());
            }
            if (!currencyList.contains(exchangeRate.getToRate())) {
                currencyList.add(exchangeRate.getToRate());
            }
        }
        Set<String> messageList = new HashSet<>();
        // 系统存在的费用科目
        List<String> feeItems = feeItemRepository.findAll().stream().map(FeeItem::getName).collect(Collectors.toList());
        // 取出所有的订单号
        List<String> orderNumberList = new ArrayList<>();
        for (Map<String, String> stringStringMap : dataList) {
            String orderNumber = stringStringMap.get("订单编号");
            if (StringUtils.hasText(orderNumber)) {
                orderNumberList.add(orderNumber);
            }
        }
        List<ZadOrder> zadOrders = zadOrderRepository.findByOrderNumberIn(orderNumberList);
        // 已经锁定的账单
        List<ZadOrder> lockOrders = zadOrders.stream().filter(zadOrder -> BillType.STATUS_YJS.equals(zadOrder.getSettlement()) || BillType.STATUS_YJS.equals(zadOrder.getOutSettlement())).collect(Collectors.toList());
        if (lockOrders.size() > 0) {
            for (ZadOrder lockOrder : lockOrders) {
                messageList.add(String.format("订单编号【%s】已封账，无法导入应收费用", lockOrder.getOrderNumber()));
            }
        }
        if (messageList.size() > 0) {
            return messageList;
        }
        int index = 1;
        for (Map<String, String> stringStringMap : dataList) {
            String orderNumber = stringStringMap.get("订单编号");
            if (StringUtils.hasText(orderNumber) && !orderNumber.toLowerCase().contains("null")) {
                boolean anyMatch = zadOrders.stream().anyMatch(order -> order.getOrderNumber().equals(orderNumber));
                if (!anyMatch) {
                    messageList.add(String.format("【应收】第【%d】行根据订单编号【%s】在系统找不到记录", index, orderNumber));
                }
            }
            int finalIndex = index;
            zadOrders.stream().filter(zadOrder -> zadOrder.getOrderNumber().equals(orderNumber)).findFirst().ifPresent(zadOrder -> {
                // 查出所有的费用
                List<OrderFee> orderFees = orderFeeRepository.findByOrderIdAndType(zadOrder.getId(), TypeStringUtils.feeInType);
                // 判断之前是否已经生成了运输费，如果没有，需要生成运输费
                OrderFee existRecord = orderFees.stream().filter(orderFee -> TypeStringUtils.feeInType.equals(orderFee.getType()) && TypeStringUtils.TRANSPORT_FEE.equals(orderFee.getFeeName())).findFirst().orElse(null);
                if (existRecord == null) {
                    existRecord = new OrderFee();
                    existRecord.setId(snowFlakeFactory.nextId("OF"));
                    existRecord.setOrderId(zadOrder.getId());
                    existRecord.setFeeName(TypeStringUtils.TRANSPORT_FEE);
                    existRecord.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                    existRecord.setType(TypeStringUtils.feeInType);
                }
                // 判断是否有填入运输费，如果没有填，则生成
                if (StringUtils.hasText(stringStringMap.get(TypeStringUtils.TRANSPORT_FEE))) {
                    existRecord.setCurrency(getCurrencyV2(stringStringMap.get(TypeStringUtils.TRANSPORT_FEE + "币种")
                            , currencyList));
                    try {
                        existRecord.setFeeCount(new Double(stringStringMap.get(TypeStringUtils.TRANSPORT_FEE)));
                    } catch (Exception e) {
                        messageList.add(String.format("【应收】第【%d】行转换运输费【%s】失败，请检查", finalIndex,
                                stringStringMap.get(TypeStringUtils.TRANSPORT_FEE)));
                    }
                } else {
                    // 自动生成运输费
                    CalculateDto calculateDto = new CalculateDto();
                    BeanUtils.copyProperties(zadOrder, calculateDto);
                    // 计费方式按照班列日期
                    calculateDto.setCalculateTime(zadOrder.getPreDepartureTime());
                    calculateDto.setCalculateType(CalculateTypeUtil.getCalculateType(zadOrder.getCalculateType()));
                    List<PriceDto> priceDtos = calculateService.calculate(calculateDto);
                    Optional<PriceDto> first = priceDtos.stream().filter(priceDto -> priceDto.getFeeName().equals(TypeStringUtils.TRANSPORT_FEE)).findFirst();
                    if (first.isPresent()) {
                        existRecord.setCurrency(first.get().getCurrency());
                        existRecord.setFeeCount(first.get().getFeeCount());
                    }
                }
                // 保存运输费
                if (messageList.size() == 0) {
                    orderFeeRepository.save(existRecord);
                }
                // 遍历集合所有字段，判断费用是否在系统中维护了的
                stringStringMap.remove("订单编号");
                stringStringMap.remove("客户");
                stringStringMap.remove("柜号");
                List<OrderFee> fees = new ArrayList<>();
                stringStringMap.forEach((key, value) -> {
                    if (TypeStringUtils.TRANSPORT_FEE.equals(key)) {
                        // 运输费不做处理，由系统自动生成
                        return;
                    }
                    if (feeItems.contains(key)) {
                        String currency = stringStringMap.get(String.format("%s币种", key));
                        if (currency == null) {
                            return;
                        }
                        // 判断是否存在相同费用科目
                        Optional<OrderFee> feeOptional = orderFees.stream().filter(fee -> fee.getFeeName().equals(key)).findFirst();
                        if (feeOptional.isPresent()) {
                            // 除运输费外，判断费用是否锁定，如果是锁定，则不允许修改
                            OrderFee fee = feeOptional.get();
                            if (!currency.equals("null") && currencyList.stream().noneMatch(cu -> cu.contains(currency))) {
                                String text = String.format("【应收】订单编号【%s】一行运输费没有找到币种【%s】，系统存在的币种有【%s】", zadOrder.getOrderNumber(), currency.trim(), String.join(",", currencyList));
                                messageList.add(text);
                                return;
                            }
                            if (!TypeStringUtils.feeLockStatus.equals(fee.getStatus())) {
                                try {
                                    fee.setCurrency(getCurrencyV2(currency, currencyList));
                                    if (getMoney(value) == null) {
                                        return;
                                    }
                                    fee.setFeeCount(new Double((getMoney(value))));
                                    // 系统发车时间为准，后面方便于统计应付费用
                                    if (zadOrder.getActDepartureTime() != null) {
                                        fee.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                                    }
                                    // 保存
                                    if (fee.getFeeCount() > 0) {
                                        orderFeeRepository.save(fee);
                                    }
                                } catch (Exception ig) {
                                    ig.printStackTrace();
                                }
                            } else {
                                String text = String.format("【应收】订单编号【%s】一行【%s】已经锁定，不允许修改", zadOrder.getOrderNumber(), fee.getFeeName());
                                messageList.add(text);
                            }
                        } else {
                            OrderFee fee = new OrderFee();
                            fee.setId(snowFlakeFactory.nextId("OF"));
                            fee.setOrderId(zadOrder.getId());
                            fee.setFeeName(key);
                            if (!currency.equals("null") && currencyList.stream().noneMatch(cu -> cu.contains(currency))) {
                                String text = String.format("【应收】订单编号【%s】一行运输费没有找到币种【%s】，系统存在的币种有【%s】", zadOrder.getOrderNumber(), currency.trim(), String.join(",", currencyList));
                                messageList.add(text);
                                return;
                            }
                            try {
                                fee.setCurrency(getCurrencyV2(currency, currencyList));
                                if (getMoney(value) == null) {
                                    return;
                                }
                                fee.setFeeCount(new Double((getMoney(value))));
                                // 系统发车时间为准，后面方便于统计应付费用
                                if (zadOrder.getActDepartureTime() != null) {
                                    fee.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                                }
                                fee.setType(TypeStringUtils.feeInType);
                                if (fee.getFeeCount() > 0) {
                                    fees.add(fee);
                                }
                            } catch (Exception ig) {
                                ig.printStackTrace();
                            }
                        }
                    }
                });
                orderFeeRepository.save(fees);
            });
            // 索引加1
            index += 1;
        }
        return messageList;
    }

    @Override
    public Set<String> batchCreateOutFee(List<Map<String, String>> dataList) {
        // 获取系统配置的币种列表
        List<ExchangeRate> exchangeRates = rateRepository.findAll();
        List<String> currencyList = new ArrayList<>();
        for (ExchangeRate exchangeRate : exchangeRates) {
            if (!currencyList.contains(exchangeRate.getFromRate())) {
                currencyList.add(exchangeRate.getFromRate());
            }
            if (!currencyList.contains(exchangeRate.getToRate())) {
                currencyList.add(exchangeRate.getToRate());
            }
        }
        Set<String> messageList = new HashSet<>();
        // 系统存在的费用科目
        List<String> feeItems = feeItemRepository.findAll().stream().map(FeeItem::getName).collect(Collectors.toList());
        // 所有供应商的简称
        List<String> carrierNames = carrierRepository.findAll().stream().map(Carrier::getName).collect(Collectors.toList());
        // 取出所有的订单号
        List<String> orderNumberList = new ArrayList<>();
        for (Map<String, String> stringStringMap : dataList) {
            String orderNumber = stringStringMap.get("订单编号");
            if (StringUtils.hasText(orderNumber)) {
                orderNumberList.add(orderNumber);
            }
        }
        List<ZadOrder> zadOrders = zadOrderRepository.findByOrderNumberIn(orderNumberList);
        // 已经锁定的账单
        List<ZadOrder> lockOrders = zadOrders.stream().filter(zadOrder -> BillType.STATUS_YJS.equals(zadOrder.getSettlement()) || BillType.STATUS_YJS.equals(zadOrder.getOutSettlement())).collect(Collectors.toList());
        if (lockOrders.size() > 0) {
            for (ZadOrder lockOrder : lockOrders) {
                messageList.add(String.format("订单编号【%s】已封账，无法导入应付费用", lockOrder.getOrderNumber()));
            }
        }
        if (messageList.size() > 0) {
            return messageList;
        }
        int index = 1;
        for (Map<String, String> stringStringMap : dataList) {
            String orderNumber = stringStringMap.get("订单编号");
            if (StringUtils.hasText(orderNumber) && !orderNumber.toLowerCase().contains("null")) {
                boolean anyMatch = zadOrders.stream().anyMatch(order -> order.getOrderNumber().equals(orderNumber));
                if (!anyMatch) {
                    messageList.add(String.format("【应付】第【%d】行根据订单编号【%s】在系统找不到记录", index, orderNumber));
                }
            }
            int finalIndex = index;
            zadOrders.stream().filter(zadOrder -> zadOrder.getOrderNumber().equals(orderNumber)).findFirst().ifPresent(zadOrder -> {
                // 查出所有的费用
                List<OrderFee> orderFees = orderFeeRepository.findByOrderIdAndType(zadOrder.getId(), TypeStringUtils.feeOutType);
                // 判断之前是否已经生成了运输费，如果没有，需要生成运输费
                OrderFee existRecord = orderFees.stream().filter(orderFee -> TypeStringUtils.feeOutType.equals(orderFee.getType()) && TypeStringUtils.TRANSPORT_FEE.equals(orderFee.getFeeName())).findFirst().orElse(null);
                if (existRecord == null) {
                    existRecord = new OrderFee();
                    existRecord.setId(snowFlakeFactory.nextId("OF"));
                    existRecord.setOrderId(zadOrder.getId());
                    existRecord.setFeeName(TypeStringUtils.TRANSPORT_FEE);
                    existRecord.setType(TypeStringUtils.feeOutType);
                }
                String carrierKey = TypeStringUtils.TRANSPORT_FEE + "供应商";
                String carrierValueExcel = stringStringMap.get(carrierKey);
                if (carrierNames.contains(carrierValueExcel)) {
                    existRecord.setCarrierId(carrierValueExcel);
                    // 如果没有填入运输费，则自动计算
                    if (StringUtils.hasText(stringStringMap.get(TypeStringUtils.TRANSPORT_FEE))) {
                        existRecord.setCurrency(getCurrencyV2(stringStringMap.get(TypeStringUtils.TRANSPORT_FEE + "币种"),
                                currencyList));
                        try {
                            existRecord.setFeeCount(new Double(stringStringMap.get(TypeStringUtils.TRANSPORT_FEE)));
                        } catch (Exception e) {
                            messageList.add(String.format("【应付】第【%d】行转换运输费【%s】失败，请检查", finalIndex,
                                    stringStringMap.get(TypeStringUtils.TRANSPORT_FEE)));
                        }
                    } else {
                        // 自动生成运输费
                        CalculateDto calculateDto = new CalculateDto();
                        BeanUtils.copyProperties(zadOrder, calculateDto);
                        // 计费方式按照提货日期
                        calculateDto.setCarrierId(carrierValueExcel);
                        calculateDto.setFeeName(TypeStringUtils.TRANSPORT_FEE);
                        calculateDto.setCalculateTime(zadOrder.getPreDepartureTime());
                        calculateDto.setNoTips(true);
                        calculateDto.setCalculateType(CalculateTypeUtil.getCalculateType(zadOrder.getCalculateType()));
                        List<PriceDto> priceDtos = calculateService.calculateOut(calculateDto);
                        Optional<PriceDto> first = priceDtos.stream().filter(priceDto -> priceDto.getFeeName().equals(TypeStringUtils.TRANSPORT_FEE)).findFirst();
                        if (first.isPresent()) {
                            existRecord.setFeeCount(first.get().getFeeCount());
                            existRecord.setCurrency(first.get().getCurrency());
                            if (zadOrder.getActDepartureTime() != null) {
                                existRecord.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                            }
                        }
                    }
                } else if (!"null".equals(carrierValueExcel)) {
                    messageList.add(String.format("【应付】第【%d】行根据供应商【%s】在系统找不到记录", finalIndex, carrierValueExcel));
                }
                if (messageList.size() == 0) {
                    // 保存运输费
                    orderFeeRepository.save(existRecord);
                }
                // 遍历集合所有字段，判断费用是否在系统中维护了的
                stringStringMap.remove("订单编号");
                stringStringMap.remove("客户");
                stringStringMap.remove("柜号");
                // 重新设置供应商
                Set<String> carrierList = new HashSet<>();
                List<OrderFee> fees = new ArrayList<>();
                for (String feeName : feeItems) {
                    // 运输费自动算
                    if (TypeStringUtils.TRANSPORT_FEE.equals(feeName)) {
                        continue;
                    }
                    if (stringStringMap.containsKey(feeName)) {
                        String value = stringStringMap.get(feeName);
                        // 判断费用是否有值，并且是数字
                        if (StringUtils.hasText(value) && !value.toLowerCase().contains("null") && !isNumber(value)) {
                            messageList.add(String.format("【应付】第【%d】行【%s】不是数字类型，请修正", finalIndex, feeName));
                            continue;
                        }
                        carrierKey = feeName + "供应商";
                        // 获取费用币种
                        String currency = stringStringMap.get(String.format("%s币种", feeName));
                        if (currency == null) {
                            continue;
                        }
                        if (!currency.equals("null") && currencyList.stream().noneMatch(cu -> cu.contains(currency))) {
                            String text = String.format("【应付】订单编号【%s】一行运输费没有找到币种【%s】，系统存在的币种有【%s】", zadOrder.getOrderNumber(), currency.trim(), String.join(",", currencyList));
                            messageList.add(text);
                            continue;
                        }
                        if (stringStringMap.containsKey(carrierKey)) {
                            carrierValueExcel = stringStringMap.get(carrierKey);
                            // 判断系统是否存在此供应商
                            if (carrierNames.contains(carrierValueExcel)) {
                                if (StringUtils.isEmpty(value) || "null".equals(value.toLowerCase().trim())) {
                                    // 如果费用没有值，那就要系统自动计算费用, 并且费用币种也要一致
                                    CalculateDto calculateDto = new CalculateDto();
                                    BeanUtils.copyProperties(zadOrder, calculateDto);
                                    calculateDto.setNoTips(true);
                                    calculateDto.setCalculateTime(zadOrder.getPreDepartureTime());
                                    calculateDto.setFeeName(feeName);
                                    calculateDto.setCarrierId(carrierValueExcel);
                                    calculateDto.setCalculateType(CalculateTypeUtil.getCalculateType(zadOrder.getCalculateType()));
                                    List<PriceDto> outPriceList = calculateService.calculateOut(calculateDto);
                                    for (PriceDto priceDto : outPriceList) {
                                        // 币种必须一致
                                        if (!priceDto.getCurrency().equals(currency)) {
                                            continue;
                                        }
                                        existRecord = orderFees.stream().filter(orderFee -> feeName.equals(orderFee.getFeeName())).findFirst().orElse(null);
                                        if (existRecord == null) {
                                            // 新增对象
                                            existRecord = new OrderFee();
                                            existRecord.setId(snowFlakeFactory.nextId("OF"));
                                            existRecord.setOrderId(zadOrder.getId());
                                            existRecord.setFeeName(priceDto.getFeeName());
                                            existRecord.setType(TypeStringUtils.feeOutType);
                                        }
                                        // 判断是否锁定
                                        if (TypeStringUtils.feeLockStatus.equals(existRecord.getStatus())) {
                                            String text = String.format("【应付】订单编号【%s】一行【%s】已经锁定，不允许修改", zadOrder.getOrderNumber(), existRecord.getFeeName());
                                            messageList.add(text);
                                            continue;
                                        }
                                        existRecord.setCarrierId(carrierValueExcel);
                                        existRecord.setFeeCount(priceDto.getFeeCount());
                                        existRecord.setCurrency(priceDto.getCurrency());
                                        if (zadOrder.getActDepartureTime() != null) {
                                            existRecord.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                                        }
                                        // 添加到保存的列表
                                        fees.add(existRecord);
                                    }
                                    carrierList.add(carrierValueExcel);
                                    // 继续下一个费用
                                    continue;
                                }
                                carrierList.add(carrierValueExcel);
                                // 判断是否存在相同费用科目
                                Optional<OrderFee> feeOptional = orderFees.stream().filter(fee -> fee.getFeeName().equals(feeName)).findFirst();
                                if (feeOptional.isPresent()) {
                                    // 除运输费外，判断费用是否锁定，如果是锁定，则不允许修改
                                    OrderFee fee = feeOptional.get();
                                    if (!TypeStringUtils.feeLockStatus.equals(fee.getStatus())) {
                                        try {
                                            fee.setCurrency(getCurrencyV2(currency, currencyList));
                                            if (getMoney(value) == null) {
                                                continue;
                                            }
                                            fee.setFeeCount(new Double((getMoney(value))));
                                            // 系统发车时间为准，后面方便于统计应付费用
                                            if (zadOrder.getActDepartureTime() != null) {
                                                fee.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                                            }
                                            fee.setCarrierId(carrierValueExcel);
                                            orderFeeRepository.save(fee);
                                        } catch (Exception ex) {
                                            ex.printStackTrace();
                                        }
                                    } else {
                                        String text = String.format("【应付】订单编号【%s】一行【%s】已经锁定，不允许修改", zadOrder.getOrderNumber(), fee.getFeeName());
                                        messageList.add(text);
                                    }
                                } else {
                                    OrderFee fee = new OrderFee();
                                    fee.setId(snowFlakeFactory.nextId("OF"));
                                    fee.setOrderId(zadOrder.getId());
                                    fee.setCarrierId(carrierValueExcel);
                                    fee.setFeeName(feeName);
                                    try {
                                        fee.setCurrency(getCurrencyV2(currency, currencyList));
                                        if (getMoney(value) == null) {
                                            continue;
                                        }
                                        fee.setFeeCount(new Double((getMoney(value))));
                                        // 系统发车时间为准，后面方便于统计应付费用
                                        if (zadOrder.getActDepartureTime() != null) {
                                            fee.setCreateTime(zadOrder.getActDepartureTime().atStartOfDay());
                                        }
                                        fee.setType(TypeStringUtils.feeOutType);
                                        if (fee.getFeeCount() > 0) {
                                            fees.add(fee);
                                        }
                                    } catch (Exception ex) {
                                        ex.printStackTrace();
                                    }
                                }
                            } else if (StringUtils.hasText(carrierValueExcel) && !carrierValueExcel.contains("null")) {
                                messageList.add(String.format("供应商【%s】不存在", carrierValueExcel));
                            }
                        }
                    }
                }
                zadOrder.setCarrierName(String.join(",", carrierList));
                zadOrderRepository.save(zadOrder);
                orderFeeRepository.save(fees);
            });
            // 索引加1
            index++;
        }
        return messageList;
    }

    @Override
    public void unSetOrder(List<String> orderIds, String type) {
        List<ZadOrder> zadOrders = zadOrderRepository.findAll(orderIds);
        List<OrderFee> orderFees = orderFeeRepository.findByOrderIdInAndType(orderIds, type);
        for (ZadOrder zadOrder : zadOrders) {
            if (TypeStringUtils.feeOutType.equals(type)) {
                zadOrder.setOutSettlement(BillType.STATUS_WJS);
            } else {
                zadOrder.setSettlement(BillType.STATUS_WJS);
            }
            orderFees.stream().filter(fee -> fee.getType().equals(type) && fee.getOrderId().equals(zadOrder.getId())).forEach(orderFee -> orderFee.setStatus(TypeStringUtils.feeUnLockStatus));
        }
        zadOrderRepository.save(zadOrders);
        orderFeeRepository.save(orderFees);
    }
}
